3 * A Backbone View that decorates the in-place edited element.
6 (function ($, Backbone, Drupal) {
10 Drupal.quickedit.FieldDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldDecorationView# */{
15 _widthAttributeIsEmpty: null,
21 'mouseenter.quickedit': 'onMouseEnter',
22 'mouseleave.quickedit': 'onMouseLeave',
24 'tabIn.quickedit': 'onMouseEnter',
25 'tabOut.quickedit': 'onMouseLeave'
31 * @augments Backbone.View
33 * @param {object} options
34 * An object with the following keys:
35 * @param {Drupal.quickedit.EditorView} options.editorView
36 * The editor object view.
38 initialize: function (options) {
39 this.editorView = options.editorView;
41 this.listenTo(this.model, 'change:state', this.stateChange);
42 this.listenTo(this.model, 'change:isChanged change:inTempStore', this.renderChanged);
49 // The el property is the field, which should not be removed. Remove the
50 // pointer to it, then call Backbone.View.prototype.remove().
52 Backbone.View.prototype.remove.call(this);
56 * Determines the actions to take given a change of state.
58 * @param {Drupal.quickedit.FieldModel} model
59 * The `FieldModel` model.
60 * @param {string} state
61 * The state of the associated field. One of
62 * {@link Drupal.quickedit.FieldModel.states}.
64 stateChange: function (model, state) {
65 var from = model.previous('state');
74 if (from !== 'inactive') {
76 if (from !== 'highlighted') {
77 this.model.set('isChanged', false);
85 this.startHighlight();
89 // NOTE: this state is not used by every editor! It's only used by
90 // those that need to interact with the server.
95 if (from !== 'activating') {
98 if (this.editorView.getQuickEditUISettings().padding) {
104 this.model.set('isChanged', true);
119 * Adds a class to the edited element that indicates whether the field has
120 * been changed by the user (i.e. locally) or the field has already been
121 * changed and stored before by the user (i.e. remotely, stored in
124 renderChanged: function () {
125 this.$el.toggleClass('quickedit-changed', this.model.get('isChanged') || this.model.get('inTempStore'));
129 * Starts hover; transitions to 'highlight' state.
131 * @param {jQuery.Event} event
134 onMouseEnter: function (event) {
136 that.model.set('state', 'highlighted');
137 event.stopPropagation();
141 * Stops hover; transitions to 'candidate' state.
143 * @param {jQuery.Event} event
146 onMouseLeave: function (event) {
148 that.model.set('state', 'candidate', {reason: 'mouseleave'});
149 event.stopPropagation();
153 * Transition to 'activating' stage.
155 * @param {jQuery.Event} event
158 onClick: function (event) {
159 this.model.set('state', 'activating');
160 event.preventDefault();
161 event.stopPropagation();
165 * Adds classes used to indicate an elements editable state.
167 decorate: function () {
168 this.$el.addClass('quickedit-candidate quickedit-editable');
172 * Removes classes used to indicate an elements editable state.
174 undecorate: function () {
175 this.$el.removeClass('quickedit-candidate quickedit-editable quickedit-highlighted quickedit-editing');
179 * Adds that class that indicates that an element is highlighted.
181 startHighlight: function () {
184 // Use a timeout to grab the next available animation frame.
185 that.$el.addClass('quickedit-highlighted');
189 * Removes the class that indicates that an element is highlighted.
191 stopHighlight: function () {
192 this.$el.removeClass('quickedit-highlighted');
196 * Removes the class that indicates that an element as editable.
198 prepareEdit: function () {
199 this.$el.addClass('quickedit-editing');
201 // Allow the field to be styled differently while editing in a pop-up
203 if (this.editorView.getQuickEditUISettings().popup) {
204 this.$el.addClass('quickedit-editor-is-popup');
209 * Removes the class that indicates that an element is being edited.
211 * Reapplies the class that indicates that a candidate editable element is
212 * again available to be edited.
214 stopEdit: function () {
215 this.$el.removeClass('quickedit-highlighted quickedit-editing');
217 // Done editing in a pop-up in-place editor; remove the class.
218 if (this.editorView.getQuickEditUISettings().popup) {
219 this.$el.removeClass('quickedit-editor-is-popup');
222 // Make the other editors show up again.
223 $('.quickedit-candidate').addClass('quickedit-editable');
227 * Adds padding around the editable element to make it pop visually.
230 // Early return if the element has already been padded.
231 if (this.$el.data('quickedit-padded')) {
236 // Add 5px padding for readability. This means we'll freeze the current
237 // width and *then* add 5px padding, hence ensuring the padding is added
239 // 1) Freeze the width (if it's not already set); don't use animations.
240 if (this.$el[0].style.width === '') {
241 this._widthAttributeIsEmpty = true;
243 .addClass('quickedit-animate-disable-width')
244 .css('width', this.$el.width());
247 // 2) Add padding; use animations.
248 var posProp = this._getPositionProperties(this.$el);
249 setTimeout(function () {
250 // Re-enable width animations (padding changes affect width too!).
251 self.$el.removeClass('quickedit-animate-disable-width');
256 'position': 'relative',
257 'top': posProp.top - 5 + 'px',
258 'left': posProp.left - 5 + 'px',
259 'padding-top': posProp['padding-top'] + 5 + 'px',
260 'padding-left': posProp['padding-left'] + 5 + 'px',
261 'padding-right': posProp['padding-right'] + 5 + 'px',
262 'padding-bottom': posProp['padding-bottom'] + 5 + 'px',
263 'margin-bottom': posProp['margin-bottom'] - 10 + 'px'
265 .data('quickedit-padded', true);
270 * Removes the padding around the element being edited when editing ceases.
272 _unpad: function () {
273 // Early return if the element has not been padded.
274 if (!this.$el.data('quickedit-padded')) {
279 // 1) Set the empty width again.
280 if (this._widthAttributeIsEmpty) {
282 .addClass('quickedit-animate-disable-width')
286 // 2) Remove padding; use animations (these will run simultaneously with)
287 // the fading out of the toolbar as its gets removed).
288 var posProp = this._getPositionProperties(this.$el);
289 setTimeout(function () {
290 // Re-enable width animations (padding changes affect width too!).
291 self.$el.removeClass('quickedit-animate-disable-width');
293 // Unpad the editable.
296 'position': 'relative',
297 'top': posProp.top + 5 + 'px',
298 'left': posProp.left + 5 + 'px',
299 'padding-top': posProp['padding-top'] - 5 + 'px',
300 'padding-left': posProp['padding-left'] - 5 + 'px',
301 'padding-right': posProp['padding-right'] - 5 + 'px',
302 'padding-bottom': posProp['padding-bottom'] - 5 + 'px',
303 'margin-bottom': posProp['margin-bottom'] + 10 + 'px'
306 // Remove the marker that indicates that this field has padding. This is
307 // done outside the timed out function above so that we don't get numerous
308 // queued functions that will remove padding before the data marker has
310 this.$el.removeData('quickedit-padded');
314 * Gets the top and left properties of an element.
316 * Convert extraneous values and information into numbers ready for
320 * The element to get position properties from.
323 * An object containing css values for the needed properties.
325 _getPositionProperties: function ($e) {
329 'top', 'left', 'bottom', 'right',
330 'padding-top', 'padding-left', 'padding-right', 'padding-bottom',
334 var propCount = props.length;
335 for (var i = 0; i < propCount; i++) {
337 r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10);
343 * Replaces blank or 'auto' CSS `position: <value>` values with "0px".
345 * @param {string} [pos]
346 * The value for a CSS position declaration.
349 * A CSS value that is valid for `position`.
351 _replaceBlankPosition: function (pos) {
352 if (pos === 'auto' || !pos) {
360 })(jQuery, Backbone, Drupal);