Chris@0
|
1 /**
|
Chris@0
|
2 * @file
|
Chris@0
|
3 * A Backbone View that provides an entity level toolbar.
|
Chris@0
|
4 */
|
Chris@0
|
5
|
Chris@0
|
6 (function ($, _, Backbone, Drupal, debounce) {
|
Chris@0
|
7 Drupal.quickedit.EntityToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityToolbarView# */{
|
Chris@0
|
8
|
Chris@0
|
9 /**
|
Chris@0
|
10 * @type {jQuery}
|
Chris@0
|
11 */
|
Chris@0
|
12 _fieldToolbarRoot: null,
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * @return {object}
|
Chris@0
|
16 * A map of events.
|
Chris@0
|
17 */
|
Chris@0
|
18 events() {
|
Chris@0
|
19 const map = {
|
Chris@0
|
20 'click button.action-save': 'onClickSave',
|
Chris@0
|
21 'click button.action-cancel': 'onClickCancel',
|
Chris@0
|
22 mouseenter: 'onMouseenter',
|
Chris@0
|
23 };
|
Chris@0
|
24 return map;
|
Chris@0
|
25 },
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * @constructs
|
Chris@0
|
29 *
|
Chris@0
|
30 * @augments Backbone.View
|
Chris@0
|
31 *
|
Chris@0
|
32 * @param {object} options
|
Chris@0
|
33 * Options to construct the view.
|
Chris@0
|
34 * @param {Drupal.quickedit.AppModel} options.appModel
|
Chris@0
|
35 * A quickedit `AppModel` to use in the view.
|
Chris@0
|
36 */
|
Chris@0
|
37 initialize(options) {
|
Chris@0
|
38 const that = this;
|
Chris@0
|
39 this.appModel = options.appModel;
|
Chris@0
|
40 this.$entity = $(this.model.get('el'));
|
Chris@0
|
41
|
Chris@0
|
42 // Rerender whenever the entity state changes.
|
Chris@0
|
43 this.listenTo(this.model, 'change:isActive change:isDirty change:state', this.render);
|
Chris@0
|
44 // Also rerender whenever a different field is highlighted or activated.
|
Chris@0
|
45 this.listenTo(this.appModel, 'change:highlightedField change:activeField', this.render);
|
Chris@0
|
46 // Rerender when a field of the entity changes state.
|
Chris@0
|
47 this.listenTo(this.model.get('fields'), 'change:state', this.fieldStateChange);
|
Chris@0
|
48
|
Chris@0
|
49 // Reposition the entity toolbar as the viewport and the position within
|
Chris@0
|
50 // the viewport changes.
|
Chris@0
|
51 $(window).on('resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit', debounce($.proxy(this.windowChangeHandler, this), 150));
|
Chris@0
|
52
|
Chris@0
|
53 // Adjust the fence placement within which the entity toolbar may be
|
Chris@0
|
54 // positioned.
|
Chris@0
|
55 $(document).on('drupalViewportOffsetChange.quickedit', (event, offsets) => {
|
Chris@0
|
56 if (that.$fence) {
|
Chris@0
|
57 that.$fence.css(offsets);
|
Chris@0
|
58 }
|
Chris@0
|
59 });
|
Chris@0
|
60
|
Chris@0
|
61 // Set the entity toolbar DOM element as the el for this view.
|
Chris@0
|
62 const $toolbar = this.buildToolbarEl();
|
Chris@0
|
63 this.setElement($toolbar);
|
Chris@0
|
64 this._fieldToolbarRoot = $toolbar.find('.quickedit-toolbar-field').get(0);
|
Chris@0
|
65
|
Chris@0
|
66 // Initial render.
|
Chris@0
|
67 this.render();
|
Chris@0
|
68 },
|
Chris@0
|
69
|
Chris@0
|
70 /**
|
Chris@0
|
71 * @inheritdoc
|
Chris@0
|
72 *
|
Chris@0
|
73 * @return {Drupal.quickedit.EntityToolbarView}
|
Chris@0
|
74 * The entity toolbar view.
|
Chris@0
|
75 */
|
Chris@0
|
76 render() {
|
Chris@0
|
77 if (this.model.get('isActive')) {
|
Chris@0
|
78 // If the toolbar container doesn't exist, create it.
|
Chris@0
|
79 const $body = $('body');
|
Chris@0
|
80 if ($body.children('#quickedit-entity-toolbar').length === 0) {
|
Chris@0
|
81 $body.append(this.$el);
|
Chris@0
|
82 }
|
Chris@0
|
83 // The fence will define a area on the screen that the entity toolbar
|
Chris@0
|
84 // will be position within.
|
Chris@0
|
85 if ($body.children('#quickedit-toolbar-fence').length === 0) {
|
Chris@0
|
86 this.$fence = $(Drupal.theme('quickeditEntityToolbarFence'))
|
Chris@0
|
87 .css(Drupal.displace())
|
Chris@0
|
88 .appendTo($body);
|
Chris@0
|
89 }
|
Chris@0
|
90 // Adds the entity title to the toolbar.
|
Chris@0
|
91 this.label();
|
Chris@0
|
92
|
Chris@0
|
93 // Show the save and cancel buttons.
|
Chris@0
|
94 this.show('ops');
|
Chris@0
|
95 // If render is being called and the toolbar is already visible, just
|
Chris@0
|
96 // reposition it.
|
Chris@0
|
97 this.position();
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 // The save button text and state varies with the state of the entity
|
Chris@0
|
101 // model.
|
Chris@0
|
102 const $button = this.$el.find('.quickedit-button.action-save');
|
Chris@0
|
103 const isDirty = this.model.get('isDirty');
|
Chris@0
|
104 // Adjust the save button according to the state of the model.
|
Chris@0
|
105 switch (this.model.get('state')) {
|
Chris@0
|
106 // Quick editing is active, but no field is being edited.
|
Chris@0
|
107 case 'opened':
|
Chris@0
|
108 // The saving throbber is not managed by AJAX system. The
|
Chris@0
|
109 // EntityToolbarView manages this visual element.
|
Chris@0
|
110 $button
|
Chris@0
|
111 .removeClass('action-saving icon-throbber icon-end')
|
Chris@0
|
112 .text(Drupal.t('Save'))
|
Chris@0
|
113 .removeAttr('disabled')
|
Chris@0
|
114 .attr('aria-hidden', !isDirty);
|
Chris@0
|
115 break;
|
Chris@0
|
116
|
Chris@0
|
117 // The changes to the fields of the entity are being committed.
|
Chris@0
|
118 case 'committing':
|
Chris@0
|
119 $button
|
Chris@0
|
120 .addClass('action-saving icon-throbber icon-end')
|
Chris@0
|
121 .text(Drupal.t('Saving'))
|
Chris@0
|
122 .attr('disabled', 'disabled');
|
Chris@0
|
123 break;
|
Chris@0
|
124
|
Chris@0
|
125 default:
|
Chris@0
|
126 $button.attr('aria-hidden', true);
|
Chris@0
|
127 break;
|
Chris@0
|
128 }
|
Chris@0
|
129
|
Chris@0
|
130 return this;
|
Chris@0
|
131 },
|
Chris@0
|
132
|
Chris@0
|
133 /**
|
Chris@0
|
134 * @inheritdoc
|
Chris@0
|
135 */
|
Chris@0
|
136 remove() {
|
Chris@0
|
137 // Remove additional DOM elements controlled by this View.
|
Chris@0
|
138 this.$fence.remove();
|
Chris@0
|
139
|
Chris@0
|
140 // Stop listening to additional events.
|
Chris@0
|
141 $(window).off('resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit');
|
Chris@0
|
142 $(document).off('drupalViewportOffsetChange.quickedit');
|
Chris@0
|
143
|
Chris@0
|
144 Backbone.View.prototype.remove.call(this);
|
Chris@0
|
145 },
|
Chris@0
|
146
|
Chris@0
|
147 /**
|
Chris@0
|
148 * Repositions the entity toolbar on window scroll and resize.
|
Chris@0
|
149 *
|
Chris@0
|
150 * @param {jQuery.Event} event
|
Chris@0
|
151 * The scroll or resize event.
|
Chris@0
|
152 */
|
Chris@0
|
153 windowChangeHandler(event) {
|
Chris@0
|
154 this.position();
|
Chris@0
|
155 },
|
Chris@0
|
156
|
Chris@0
|
157 /**
|
Chris@0
|
158 * Determines the actions to take given a change of state.
|
Chris@0
|
159 *
|
Chris@0
|
160 * @param {Drupal.quickedit.FieldModel} model
|
Chris@0
|
161 * The `FieldModel` model.
|
Chris@0
|
162 * @param {string} state
|
Chris@0
|
163 * The state of the associated field. One of
|
Chris@0
|
164 * {@link Drupal.quickedit.FieldModel.states}.
|
Chris@0
|
165 */
|
Chris@0
|
166 fieldStateChange(model, state) {
|
Chris@0
|
167 switch (state) {
|
Chris@0
|
168 case 'active':
|
Chris@0
|
169 this.render();
|
Chris@0
|
170 break;
|
Chris@0
|
171
|
Chris@0
|
172 case 'invalid':
|
Chris@0
|
173 this.render();
|
Chris@0
|
174 break;
|
Chris@0
|
175 }
|
Chris@0
|
176 },
|
Chris@0
|
177
|
Chris@0
|
178 /**
|
Chris@0
|
179 * Uses the jQuery.ui.position() method to position the entity toolbar.
|
Chris@0
|
180 *
|
Chris@0
|
181 * @param {HTMLElement} [element]
|
Chris@0
|
182 * The element against which the entity toolbar is positioned.
|
Chris@0
|
183 */
|
Chris@0
|
184 position(element) {
|
Chris@0
|
185 clearTimeout(this.timer);
|
Chris@0
|
186
|
Chris@0
|
187 const that = this;
|
Chris@0
|
188 // Vary the edge of the positioning according to the direction of language
|
Chris@0
|
189 // in the document.
|
Chris@0
|
190 const edge = (document.documentElement.dir === 'rtl') ? 'right' : 'left';
|
Chris@0
|
191 // A time unit to wait until the entity toolbar is repositioned.
|
Chris@0
|
192 let delay = 0;
|
Chris@0
|
193 // Determines what check in the series of checks below should be
|
Chris@0
|
194 // evaluated.
|
Chris@0
|
195 let check = 0;
|
Chris@0
|
196 // When positioned against an active field that has padding, we should
|
Chris@0
|
197 // ignore that padding when positioning the toolbar, to not unnecessarily
|
Chris@0
|
198 // move the toolbar horizontally, which feels annoying.
|
Chris@0
|
199 let horizontalPadding = 0;
|
Chris@0
|
200 let of;
|
Chris@0
|
201 let activeField;
|
Chris@0
|
202 let highlightedField;
|
Chris@0
|
203 // There are several elements in the page that the entity toolbar might be
|
Chris@0
|
204 // positioned against. They are considered below in a priority order.
|
Chris@0
|
205 do {
|
Chris@0
|
206 switch (check) {
|
Chris@0
|
207 case 0:
|
Chris@0
|
208 // Position against a specific element.
|
Chris@0
|
209 of = element;
|
Chris@0
|
210 break;
|
Chris@0
|
211
|
Chris@0
|
212 case 1:
|
Chris@0
|
213 // Position against a form container.
|
Chris@0
|
214 activeField = Drupal.quickedit.app.model.get('activeField');
|
Chris@0
|
215 of = activeField && activeField.editorView && activeField.editorView.$formContainer && activeField.editorView.$formContainer.find('.quickedit-form');
|
Chris@0
|
216 break;
|
Chris@0
|
217
|
Chris@0
|
218 case 2:
|
Chris@0
|
219 // Position against an active field.
|
Chris@0
|
220 of = activeField && activeField.editorView && activeField.editorView.getEditedElement();
|
Chris@0
|
221 if (activeField && activeField.editorView && activeField.editorView.getQuickEditUISettings().padding) {
|
Chris@0
|
222 horizontalPadding = 5;
|
Chris@0
|
223 }
|
Chris@0
|
224 break;
|
Chris@0
|
225
|
Chris@0
|
226 case 3:
|
Chris@0
|
227 // Position against a highlighted field.
|
Chris@0
|
228 highlightedField = Drupal.quickedit.app.model.get('highlightedField');
|
Chris@0
|
229 of = highlightedField && highlightedField.editorView && highlightedField.editorView.getEditedElement();
|
Chris@0
|
230 delay = 250;
|
Chris@0
|
231 break;
|
Chris@0
|
232
|
Chris@0
|
233 default: {
|
Chris@0
|
234 const fieldModels = this.model.get('fields').models;
|
Chris@0
|
235 let topMostPosition = 1000000;
|
Chris@0
|
236 let topMostField = null;
|
Chris@0
|
237 // Position against the topmost field.
|
Chris@0
|
238 for (let i = 0; i < fieldModels.length; i++) {
|
Chris@0
|
239 const pos = fieldModels[i].get('el').getBoundingClientRect().top;
|
Chris@0
|
240 if (pos < topMostPosition) {
|
Chris@0
|
241 topMostPosition = pos;
|
Chris@0
|
242 topMostField = fieldModels[i];
|
Chris@0
|
243 }
|
Chris@0
|
244 }
|
Chris@0
|
245 of = topMostField.get('el');
|
Chris@0
|
246 delay = 50;
|
Chris@0
|
247 break;
|
Chris@0
|
248 }
|
Chris@0
|
249 }
|
Chris@0
|
250 // Prepare to check the next possible element to position against.
|
Chris@0
|
251 check++;
|
Chris@0
|
252 } while (!of);
|
Chris@0
|
253
|
Chris@0
|
254 /**
|
Chris@0
|
255 * Refines the positioning algorithm of jquery.ui.position().
|
Chris@0
|
256 *
|
Chris@0
|
257 * Invoked as the 'using' callback of jquery.ui.position() in
|
Chris@0
|
258 * positionToolbar().
|
Chris@0
|
259 *
|
Chris@0
|
260 * @param {*} view
|
Chris@0
|
261 * The view the positions will be calculated from.
|
Chris@0
|
262 * @param {object} suggested
|
Chris@0
|
263 * A hash of top and left values for the position that should be set. It
|
Chris@0
|
264 * can be forwarded to .css() or .animate().
|
Chris@0
|
265 * @param {object} info
|
Chris@0
|
266 * The position and dimensions of both the 'my' element and the 'of'
|
Chris@0
|
267 * elements, as well as calculations to their relative position. This
|
Chris@0
|
268 * object contains the following properties:
|
Chris@0
|
269 * @param {object} info.element
|
Chris@0
|
270 * A hash that contains information about the HTML element that will be
|
Chris@0
|
271 * positioned. Also known as the 'my' element.
|
Chris@0
|
272 * @param {object} info.target
|
Chris@0
|
273 * A hash that contains information about the HTML element that the
|
Chris@0
|
274 * 'my' element will be positioned against. Also known as the 'of'
|
Chris@0
|
275 * element.
|
Chris@0
|
276 */
|
Chris@0
|
277 function refinePosition(view, suggested, info) {
|
Chris@0
|
278 // Determine if the pointer should be on the top or bottom.
|
Chris@0
|
279 const isBelow = suggested.top > info.target.top;
|
Chris@0
|
280 info.element.element.toggleClass('quickedit-toolbar-pointer-top', isBelow);
|
Chris@0
|
281 // Don't position the toolbar past the first or last editable field if
|
Chris@0
|
282 // the entity is the target.
|
Chris@0
|
283 if (view.$entity[0] === info.target.element[0]) {
|
Chris@0
|
284 // Get the first or last field according to whether the toolbar is
|
Chris@0
|
285 // above or below the entity.
|
Chris@0
|
286 const $field = view.$entity.find('.quickedit-editable').eq((isBelow) ? -1 : 0);
|
Chris@0
|
287 if ($field.length > 0) {
|
Chris@0
|
288 suggested.top = (isBelow) ? ($field.offset().top + $field.outerHeight(true)) : $field.offset().top - info.element.element.outerHeight(true);
|
Chris@0
|
289 }
|
Chris@0
|
290 }
|
Chris@0
|
291 // Don't let the toolbar go outside the fence.
|
Chris@0
|
292 const fenceTop = view.$fence.offset().top;
|
Chris@0
|
293 const fenceHeight = view.$fence.height();
|
Chris@0
|
294 const toolbarHeight = info.element.element.outerHeight(true);
|
Chris@0
|
295 if (suggested.top < fenceTop) {
|
Chris@0
|
296 suggested.top = fenceTop;
|
Chris@0
|
297 }
|
Chris@0
|
298 else if ((suggested.top + toolbarHeight) > (fenceTop + fenceHeight)) {
|
Chris@0
|
299 suggested.top = (fenceTop + fenceHeight) - toolbarHeight;
|
Chris@0
|
300 }
|
Chris@0
|
301 // Position the toolbar.
|
Chris@0
|
302 info.element.element.css({
|
Chris@0
|
303 left: Math.floor(suggested.left),
|
Chris@0
|
304 top: Math.floor(suggested.top),
|
Chris@0
|
305 });
|
Chris@0
|
306 }
|
Chris@0
|
307
|
Chris@0
|
308 /**
|
Chris@0
|
309 * Calls the jquery.ui.position() method on the $el of this view.
|
Chris@0
|
310 */
|
Chris@0
|
311 function positionToolbar() {
|
Chris@0
|
312 that.$el
|
Chris@0
|
313 .position({
|
Chris@0
|
314 my: `${edge} bottom`,
|
Chris@0
|
315 // Move the toolbar 1px towards the start edge of the 'of' element,
|
Chris@0
|
316 // plus any horizontal padding that may have been added to the
|
Chris@0
|
317 // element that is being added, to prevent unwanted horizontal
|
Chris@0
|
318 // movement.
|
Chris@0
|
319 at: `${edge}+${1 + horizontalPadding} top`,
|
Chris@0
|
320 of,
|
Chris@0
|
321 collision: 'flipfit',
|
Chris@0
|
322 using: refinePosition.bind(null, that),
|
Chris@0
|
323 within: that.$fence,
|
Chris@0
|
324 })
|
Chris@0
|
325 // Resize the toolbar to match the dimensions of the field, up to a
|
Chris@0
|
326 // maximum width that is equal to 90% of the field's width.
|
Chris@0
|
327 .css({
|
Chris@0
|
328 'max-width': (document.documentElement.clientWidth < 450) ? document.documentElement.clientWidth : 450,
|
Chris@0
|
329 // Set a minimum width of 240px for the entity toolbar, or the width
|
Chris@0
|
330 // of the client if it is less than 240px, so that the toolbar
|
Chris@0
|
331 // never folds up into a squashed and jumbled mess.
|
Chris@0
|
332 'min-width': (document.documentElement.clientWidth < 240) ? document.documentElement.clientWidth : 240,
|
Chris@0
|
333 width: '100%',
|
Chris@0
|
334 });
|
Chris@0
|
335 }
|
Chris@0
|
336
|
Chris@0
|
337 // Uses the jQuery.ui.position() method. Use a timeout to move the toolbar
|
Chris@0
|
338 // only after the user has focused on an editable for 250ms. This prevents
|
Chris@0
|
339 // the toolbar from jumping around the screen.
|
Chris@0
|
340 this.timer = setTimeout(() => {
|
Chris@0
|
341 // Render the position in the next execution cycle, so that animations
|
Chris@0
|
342 // on the field have time to process. This is not strictly speaking, a
|
Chris@0
|
343 // guarantee that all animations will be finished, but it's a simple
|
Chris@0
|
344 // way to get better positioning without too much additional code.
|
Chris@0
|
345 _.defer(positionToolbar);
|
Chris@0
|
346 }, delay);
|
Chris@0
|
347 },
|
Chris@0
|
348
|
Chris@0
|
349 /**
|
Chris@0
|
350 * Set the model state to 'saving' when the save button is clicked.
|
Chris@0
|
351 *
|
Chris@0
|
352 * @param {jQuery.Event} event
|
Chris@0
|
353 * The click event.
|
Chris@0
|
354 */
|
Chris@0
|
355 onClickSave(event) {
|
Chris@0
|
356 event.stopPropagation();
|
Chris@0
|
357 event.preventDefault();
|
Chris@0
|
358 // Save the model.
|
Chris@0
|
359 this.model.set('state', 'committing');
|
Chris@0
|
360 },
|
Chris@0
|
361
|
Chris@0
|
362 /**
|
Chris@0
|
363 * Sets the model state to candidate when the cancel button is clicked.
|
Chris@0
|
364 *
|
Chris@0
|
365 * @param {jQuery.Event} event
|
Chris@0
|
366 * The click event.
|
Chris@0
|
367 */
|
Chris@0
|
368 onClickCancel(event) {
|
Chris@0
|
369 event.preventDefault();
|
Chris@0
|
370 this.model.set('state', 'deactivating');
|
Chris@0
|
371 },
|
Chris@0
|
372
|
Chris@0
|
373 /**
|
Chris@0
|
374 * Clears the timeout that will eventually reposition the entity toolbar.
|
Chris@0
|
375 *
|
Chris@0
|
376 * Without this, it may reposition itself, away from the user's cursor!
|
Chris@0
|
377 *
|
Chris@0
|
378 * @param {jQuery.Event} event
|
Chris@0
|
379 * The mouse event.
|
Chris@0
|
380 */
|
Chris@0
|
381 onMouseenter(event) {
|
Chris@0
|
382 clearTimeout(this.timer);
|
Chris@0
|
383 },
|
Chris@0
|
384
|
Chris@0
|
385 /**
|
Chris@0
|
386 * Builds the entity toolbar HTML; attaches to DOM; sets starting position.
|
Chris@0
|
387 *
|
Chris@0
|
388 * @return {jQuery}
|
Chris@0
|
389 * The toolbar element.
|
Chris@0
|
390 */
|
Chris@0
|
391 buildToolbarEl() {
|
Chris@0
|
392 const $toolbar = $(Drupal.theme('quickeditEntityToolbar', {
|
Chris@0
|
393 id: 'quickedit-entity-toolbar',
|
Chris@0
|
394 }));
|
Chris@0
|
395
|
Chris@0
|
396 $toolbar
|
Chris@0
|
397 .find('.quickedit-toolbar-entity')
|
Chris@0
|
398 // Append the "ops" toolgroup into the toolbar.
|
Chris@0
|
399 .prepend(Drupal.theme('quickeditToolgroup', {
|
Chris@0
|
400 classes: ['ops'],
|
Chris@0
|
401 buttons: [
|
Chris@0
|
402 {
|
Chris@0
|
403 label: Drupal.t('Save'),
|
Chris@0
|
404 type: 'submit',
|
Chris@0
|
405 classes: 'action-save quickedit-button icon',
|
Chris@0
|
406 attributes: {
|
Chris@0
|
407 'aria-hidden': true,
|
Chris@0
|
408 },
|
Chris@0
|
409 },
|
Chris@0
|
410 {
|
Chris@0
|
411 label: Drupal.t('Close'),
|
Chris@0
|
412 classes: 'action-cancel quickedit-button icon icon-close icon-only',
|
Chris@0
|
413 },
|
Chris@0
|
414 ],
|
Chris@0
|
415 }));
|
Chris@0
|
416
|
Chris@0
|
417 // Give the toolbar a sensible starting position so that it doesn't
|
Chris@0
|
418 // animate on to the screen from a far off corner.
|
Chris@0
|
419 $toolbar
|
Chris@0
|
420 .css({
|
Chris@0
|
421 left: this.$entity.offset().left,
|
Chris@0
|
422 top: this.$entity.offset().top,
|
Chris@0
|
423 });
|
Chris@0
|
424
|
Chris@0
|
425 return $toolbar;
|
Chris@0
|
426 },
|
Chris@0
|
427
|
Chris@0
|
428 /**
|
Chris@0
|
429 * Returns the DOM element that fields will attach their toolbars to.
|
Chris@0
|
430 *
|
Chris@0
|
431 * @return {jQuery}
|
Chris@0
|
432 * The DOM element that fields will attach their toolbars to.
|
Chris@0
|
433 */
|
Chris@0
|
434 getToolbarRoot() {
|
Chris@0
|
435 return this._fieldToolbarRoot;
|
Chris@0
|
436 },
|
Chris@0
|
437
|
Chris@0
|
438 /**
|
Chris@0
|
439 * Generates a state-dependent label for the entity toolbar.
|
Chris@0
|
440 */
|
Chris@0
|
441 label() {
|
Chris@0
|
442 // The entity label.
|
Chris@0
|
443 let label = '';
|
Chris@0
|
444 const entityLabel = this.model.get('label');
|
Chris@0
|
445
|
Chris@0
|
446 // Label of an active field, if it exists.
|
Chris@0
|
447 const activeField = Drupal.quickedit.app.model.get('activeField');
|
Chris@0
|
448 const activeFieldLabel = activeField && activeField.get('metadata').label;
|
Chris@0
|
449 // Label of a highlighted field, if it exists.
|
Chris@0
|
450 const highlightedField = Drupal.quickedit.app.model.get('highlightedField');
|
Chris@0
|
451 const highlightedFieldLabel = highlightedField && highlightedField.get('metadata').label;
|
Chris@0
|
452 // The label is constructed in a priority order.
|
Chris@0
|
453 if (activeFieldLabel) {
|
Chris@0
|
454 label = Drupal.theme('quickeditEntityToolbarLabel', {
|
Chris@0
|
455 entityLabel,
|
Chris@0
|
456 fieldLabel: activeFieldLabel,
|
Chris@0
|
457 });
|
Chris@0
|
458 }
|
Chris@0
|
459 else if (highlightedFieldLabel) {
|
Chris@0
|
460 label = Drupal.theme('quickeditEntityToolbarLabel', {
|
Chris@0
|
461 entityLabel,
|
Chris@0
|
462 fieldLabel: highlightedFieldLabel,
|
Chris@0
|
463 });
|
Chris@0
|
464 }
|
Chris@0
|
465 else {
|
Chris@0
|
466 // @todo Add XSS regression test coverage in https://www.drupal.org/node/2547437
|
Chris@0
|
467 label = Drupal.checkPlain(entityLabel);
|
Chris@0
|
468 }
|
Chris@0
|
469
|
Chris@0
|
470 this.$el
|
Chris@0
|
471 .find('.quickedit-toolbar-label')
|
Chris@0
|
472 .html(label);
|
Chris@0
|
473 },
|
Chris@0
|
474
|
Chris@0
|
475 /**
|
Chris@0
|
476 * Adds classes to a toolgroup.
|
Chris@0
|
477 *
|
Chris@0
|
478 * @param {string} toolgroup
|
Chris@0
|
479 * A toolgroup name.
|
Chris@0
|
480 * @param {string} classes
|
Chris@0
|
481 * A string of space-delimited class names that will be applied to the
|
Chris@0
|
482 * wrapping element of the toolbar group.
|
Chris@0
|
483 */
|
Chris@0
|
484 addClass(toolgroup, classes) {
|
Chris@0
|
485 this._find(toolgroup).addClass(classes);
|
Chris@0
|
486 },
|
Chris@0
|
487
|
Chris@0
|
488 /**
|
Chris@0
|
489 * Removes classes from a toolgroup.
|
Chris@0
|
490 *
|
Chris@0
|
491 * @param {string} toolgroup
|
Chris@0
|
492 * A toolgroup name.
|
Chris@0
|
493 * @param {string} classes
|
Chris@0
|
494 * A string of space-delimited class names that will be removed from the
|
Chris@0
|
495 * wrapping element of the toolbar group.
|
Chris@0
|
496 */
|
Chris@0
|
497 removeClass(toolgroup, classes) {
|
Chris@0
|
498 this._find(toolgroup).removeClass(classes);
|
Chris@0
|
499 },
|
Chris@0
|
500
|
Chris@0
|
501 /**
|
Chris@0
|
502 * Finds a toolgroup.
|
Chris@0
|
503 *
|
Chris@0
|
504 * @param {string} toolgroup
|
Chris@0
|
505 * A toolgroup name.
|
Chris@0
|
506 *
|
Chris@0
|
507 * @return {jQuery}
|
Chris@0
|
508 * The toolgroup DOM element.
|
Chris@0
|
509 */
|
Chris@0
|
510 _find(toolgroup) {
|
Chris@0
|
511 return this.$el.find(`.quickedit-toolbar .quickedit-toolgroup.${toolgroup}`);
|
Chris@0
|
512 },
|
Chris@0
|
513
|
Chris@0
|
514 /**
|
Chris@0
|
515 * Shows a toolgroup.
|
Chris@0
|
516 *
|
Chris@0
|
517 * @param {string} toolgroup
|
Chris@0
|
518 * A toolgroup name.
|
Chris@0
|
519 */
|
Chris@0
|
520 show(toolgroup) {
|
Chris@0
|
521 this.$el.removeClass('quickedit-animate-invisible');
|
Chris@0
|
522 },
|
Chris@0
|
523
|
Chris@0
|
524 });
|
Chris@0
|
525 }(jQuery, _, Backbone, Drupal, Drupal.debounce));
|