* A Backbone View that decorates the in-place edited element.
*/
-(function ($, Backbone, Drupal) {
- Drupal.quickedit.FieldDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldDecorationView# */{
-
- /**
- * @type {null}
- */
- _widthAttributeIsEmpty: null,
-
- /**
- * @type {object}
- */
- events: {
- 'mouseenter.quickedit': 'onMouseEnter',
- 'mouseleave.quickedit': 'onMouseLeave',
- click: 'onClick',
- 'tabIn.quickedit': 'onMouseEnter',
- 'tabOut.quickedit': 'onMouseLeave',
- },
-
- /**
- * @constructs
- *
- * @augments Backbone.View
- *
- * @param {object} options
- * An object with the following keys:
- * @param {Drupal.quickedit.EditorView} options.editorView
- * The editor object view.
- */
- initialize(options) {
- this.editorView = options.editorView;
-
- this.listenTo(this.model, 'change:state', this.stateChange);
- this.listenTo(this.model, 'change:isChanged change:inTempStore', this.renderChanged);
- },
-
- /**
- * @inheritdoc
- */
- remove() {
- // The el property is the field, which should not be removed. Remove the
- // pointer to it, then call Backbone.View.prototype.remove().
- this.setElement();
- Backbone.View.prototype.remove.call(this);
- },
-
- /**
- * Determines the actions to take given a change of state.
- *
- * @param {Drupal.quickedit.FieldModel} model
- * The `FieldModel` model.
- * @param {string} state
- * The state of the associated field. One of
- * {@link Drupal.quickedit.FieldModel.states}.
- */
- stateChange(model, state) {
- const from = model.previous('state');
- const to = state;
- switch (to) {
- case 'inactive':
- this.undecorate();
- break;
-
- case 'candidate':
- this.decorate();
- if (from !== 'inactive') {
- this.stopHighlight();
- if (from !== 'highlighted') {
- this.model.set('isChanged', false);
- this.stopEdit();
+(function($, Backbone, Drupal) {
+ Drupal.quickedit.FieldDecorationView = Backbone.View.extend(
+ /** @lends Drupal.quickedit.FieldDecorationView# */ {
+ /**
+ * @type {null}
+ */
+ _widthAttributeIsEmpty: null,
+
+ /**
+ * @type {object}
+ */
+ events: {
+ 'mouseenter.quickedit': 'onMouseEnter',
+ 'mouseleave.quickedit': 'onMouseLeave',
+ click: 'onClick',
+ 'tabIn.quickedit': 'onMouseEnter',
+ 'tabOut.quickedit': 'onMouseLeave',
+ },
+
+ /**
+ * @constructs
+ *
+ * @augments Backbone.View
+ *
+ * @param {object} options
+ * An object with the following keys:
+ * @param {Drupal.quickedit.EditorView} options.editorView
+ * The editor object view.
+ */
+ initialize(options) {
+ this.editorView = options.editorView;
+
+ this.listenTo(this.model, 'change:state', this.stateChange);
+ this.listenTo(
+ this.model,
+ 'change:isChanged change:inTempStore',
+ this.renderChanged,
+ );
+ },
+
+ /**
+ * @inheritdoc
+ */
+ remove() {
+ // The el property is the field, which should not be removed. Remove the
+ // pointer to it, then call Backbone.View.prototype.remove().
+ this.setElement();
+ Backbone.View.prototype.remove.call(this);
+ },
+
+ /**
+ * Determines the actions to take given a change of state.
+ *
+ * @param {Drupal.quickedit.FieldModel} model
+ * The `FieldModel` model.
+ * @param {string} state
+ * The state of the associated field. One of
+ * {@link Drupal.quickedit.FieldModel.states}.
+ */
+ stateChange(model, state) {
+ const from = model.previous('state');
+ const to = state;
+ switch (to) {
+ case 'inactive':
+ this.undecorate();
+ break;
+
+ case 'candidate':
+ this.decorate();
+ if (from !== 'inactive') {
+ this.stopHighlight();
+ if (from !== 'highlighted') {
+ this.model.set('isChanged', false);
+ this.stopEdit();
+ }
}
- }
- this._unpad();
- break;
-
- case 'highlighted':
- this.startHighlight();
- break;
-
- case 'activating':
- // NOTE: this state is not used by every editor! It's only used by
- // those that need to interact with the server.
- this.prepareEdit();
- break;
-
- case 'active':
- if (from !== 'activating') {
- this.prepareEdit();
- }
- if (this.editorView.getQuickEditUISettings().padding) {
- this._pad();
- }
- break;
-
- case 'changed':
- this.model.set('isChanged', true);
- break;
-
- case 'saving':
- break;
+ this._unpad();
+ break;
- case 'saved':
- break;
-
- case 'invalid':
- break;
- }
- },
-
- /**
- * Adds a class to the edited element that indicates whether the field has
- * been changed by the user (i.e. locally) or the field has already been
- * changed and stored before by the user (i.e. remotely, stored in
- * PrivateTempStore).
- */
- renderChanged() {
- this.$el.toggleClass('quickedit-changed', this.model.get('isChanged') || this.model.get('inTempStore'));
- },
-
- /**
- * Starts hover; transitions to 'highlight' state.
- *
- * @param {jQuery.Event} event
- * The mouse event.
- */
- onMouseEnter(event) {
- const that = this;
- that.model.set('state', 'highlighted');
- event.stopPropagation();
- },
-
- /**
- * Stops hover; transitions to 'candidate' state.
- *
- * @param {jQuery.Event} event
- * The mouse event.
- */
- onMouseLeave(event) {
- const that = this;
- that.model.set('state', 'candidate', { reason: 'mouseleave' });
- event.stopPropagation();
- },
-
- /**
- * Transition to 'activating' stage.
- *
- * @param {jQuery.Event} event
- * The click event.
- */
- onClick(event) {
- this.model.set('state', 'activating');
- event.preventDefault();
- event.stopPropagation();
- },
-
- /**
- * Adds classes used to indicate an elements editable state.
- */
- decorate() {
- this.$el.addClass('quickedit-candidate quickedit-editable');
- },
+ case 'highlighted':
+ this.startHighlight();
+ break;
- /**
- * Removes classes used to indicate an elements editable state.
- */
- undecorate() {
- this.$el.removeClass('quickedit-candidate quickedit-editable quickedit-highlighted quickedit-editing');
- },
-
- /**
- * Adds that class that indicates that an element is highlighted.
- */
- startHighlight() {
- // Animations.
- const that = this;
- // Use a timeout to grab the next available animation frame.
- that.$el.addClass('quickedit-highlighted');
- },
-
- /**
- * Removes the class that indicates that an element is highlighted.
- */
- stopHighlight() {
- this.$el.removeClass('quickedit-highlighted');
- },
-
- /**
- * Removes the class that indicates that an element as editable.
- */
- prepareEdit() {
- this.$el.addClass('quickedit-editing');
-
- // Allow the field to be styled differently while editing in a pop-up
- // in-place editor.
- if (this.editorView.getQuickEditUISettings().popup) {
- this.$el.addClass('quickedit-editor-is-popup');
- }
- },
-
- /**
- * Removes the class that indicates that an element is being edited.
- *
- * Reapplies the class that indicates that a candidate editable element is
- * again available to be edited.
- */
- stopEdit() {
- this.$el.removeClass('quickedit-highlighted quickedit-editing');
-
- // Done editing in a pop-up in-place editor; remove the class.
- if (this.editorView.getQuickEditUISettings().popup) {
- this.$el.removeClass('quickedit-editor-is-popup');
- }
-
- // Make the other editors show up again.
- $('.quickedit-candidate').addClass('quickedit-editable');
- },
-
- /**
- * Adds padding around the editable element to make it pop visually.
- */
- _pad() {
- // Early return if the element has already been padded.
- if (this.$el.data('quickedit-padded')) {
- return;
- }
- const self = this;
-
- // Add 5px padding for readability. This means we'll freeze the current
- // width and *then* add 5px padding, hence ensuring the padding is added
- // "on the outside".
- // 1) Freeze the width (if it's not already set); don't use animations.
- if (this.$el[0].style.width === '') {
- this._widthAttributeIsEmpty = true;
- this.$el
- .addClass('quickedit-animate-disable-width')
- .css('width', this.$el.width());
- }
-
- // 2) Add padding; use animations.
- const posProp = this._getPositionProperties(this.$el);
- setTimeout(() => {
- // Re-enable width animations (padding changes affect width too!).
- self.$el.removeClass('quickedit-animate-disable-width');
-
- // Pad the editable.
- self.$el
- .css({
- position: 'relative',
- top: `${posProp.top - 5}px`,
- left: `${posProp.left - 5}px`,
- 'padding-top': `${posProp['padding-top'] + 5}px`,
- 'padding-left': `${posProp['padding-left'] + 5}px`,
- 'padding-right': `${posProp['padding-right'] + 5}px`,
- 'padding-bottom': `${posProp['padding-bottom'] + 5}px`,
- 'margin-bottom': `${posProp['margin-bottom'] - 10}px`,
- })
- .data('quickedit-padded', true);
- }, 0);
- },
+ case 'activating':
+ // NOTE: this state is not used by every editor! It's only used by
+ // those that need to interact with the server.
+ this.prepareEdit();
+ break;
- /**
- * Removes the padding around the element being edited when editing ceases.
- */
- _unpad() {
- // Early return if the element has not been padded.
- if (!this.$el.data('quickedit-padded')) {
- return;
- }
- const self = this;
-
- // 1) Set the empty width again.
- if (this._widthAttributeIsEmpty) {
- this.$el
- .addClass('quickedit-animate-disable-width')
- .css('width', '');
- }
-
- // 2) Remove padding; use animations (these will run simultaneously with)
- // the fading out of the toolbar as its gets removed).
- const posProp = this._getPositionProperties(this.$el);
- setTimeout(() => {
- // Re-enable width animations (padding changes affect width too!).
- self.$el.removeClass('quickedit-animate-disable-width');
-
- // Unpad the editable.
- self.$el
- .css({
+ case 'active':
+ if (from !== 'activating') {
+ this.prepareEdit();
+ }
+ if (this.editorView.getQuickEditUISettings().padding) {
+ this._pad();
+ }
+ break;
+
+ case 'changed':
+ this.model.set('isChanged', true);
+ break;
+
+ case 'saving':
+ break;
+
+ case 'saved':
+ break;
+
+ case 'invalid':
+ break;
+ }
+ },
+
+ /**
+ * Adds a class to the edited element that indicates whether the field has
+ * been changed by the user (i.e. locally) or the field has already been
+ * changed and stored before by the user (i.e. remotely, stored in
+ * PrivateTempStore).
+ */
+ renderChanged() {
+ this.$el.toggleClass(
+ 'quickedit-changed',
+ this.model.get('isChanged') || this.model.get('inTempStore'),
+ );
+ },
+
+ /**
+ * Starts hover; transitions to 'highlight' state.
+ *
+ * @param {jQuery.Event} event
+ * The mouse event.
+ */
+ onMouseEnter(event) {
+ const that = this;
+ that.model.set('state', 'highlighted');
+ event.stopPropagation();
+ },
+
+ /**
+ * Stops hover; transitions to 'candidate' state.
+ *
+ * @param {jQuery.Event} event
+ * The mouse event.
+ */
+ onMouseLeave(event) {
+ const that = this;
+ that.model.set('state', 'candidate', { reason: 'mouseleave' });
+ event.stopPropagation();
+ },
+
+ /**
+ * Transition to 'activating' stage.
+ *
+ * @param {jQuery.Event} event
+ * The click event.
+ */
+ onClick(event) {
+ this.model.set('state', 'activating');
+ event.preventDefault();
+ event.stopPropagation();
+ },
+
+ /**
+ * Adds classes used to indicate an elements editable state.
+ */
+ decorate() {
+ this.$el.addClass('quickedit-candidate quickedit-editable');
+ },
+
+ /**
+ * Removes classes used to indicate an elements editable state.
+ */
+ undecorate() {
+ this.$el.removeClass(
+ 'quickedit-candidate quickedit-editable quickedit-highlighted quickedit-editing',
+ );
+ },
+
+ /**
+ * Adds that class that indicates that an element is highlighted.
+ */
+ startHighlight() {
+ // Animations.
+ const that = this;
+ // Use a timeout to grab the next available animation frame.
+ that.$el.addClass('quickedit-highlighted');
+ },
+
+ /**
+ * Removes the class that indicates that an element is highlighted.
+ */
+ stopHighlight() {
+ this.$el.removeClass('quickedit-highlighted');
+ },
+
+ /**
+ * Removes the class that indicates that an element as editable.
+ */
+ prepareEdit() {
+ this.$el.addClass('quickedit-editing');
+
+ // Allow the field to be styled differently while editing in a pop-up
+ // in-place editor.
+ if (this.editorView.getQuickEditUISettings().popup) {
+ this.$el.addClass('quickedit-editor-is-popup');
+ }
+ },
+
+ /**
+ * Removes the class that indicates that an element is being edited.
+ *
+ * Reapplies the class that indicates that a candidate editable element is
+ * again available to be edited.
+ */
+ stopEdit() {
+ this.$el.removeClass('quickedit-highlighted quickedit-editing');
+
+ // Done editing in a pop-up in-place editor; remove the class.
+ if (this.editorView.getQuickEditUISettings().popup) {
+ this.$el.removeClass('quickedit-editor-is-popup');
+ }
+
+ // Make the other editors show up again.
+ $('.quickedit-candidate').addClass('quickedit-editable');
+ },
+
+ /**
+ * Adds padding around the editable element to make it pop visually.
+ */
+ _pad() {
+ // Early return if the element has already been padded.
+ if (this.$el.data('quickedit-padded')) {
+ return;
+ }
+ const self = this;
+
+ // Add 5px padding for readability. This means we'll freeze the current
+ // width and *then* add 5px padding, hence ensuring the padding is added
+ // "on the outside".
+ // 1) Freeze the width (if it's not already set); don't use animations.
+ if (this.$el[0].style.width === '') {
+ this._widthAttributeIsEmpty = true;
+ this.$el
+ .addClass('quickedit-animate-disable-width')
+ .css('width', this.$el.width());
+ }
+
+ // 2) Add padding; use animations.
+ const posProp = this._getPositionProperties(this.$el);
+ setTimeout(() => {
+ // Re-enable width animations (padding changes affect width too!).
+ self.$el.removeClass('quickedit-animate-disable-width');
+
+ // Pad the editable.
+ self.$el
+ .css({
+ position: 'relative',
+ top: `${posProp.top - 5}px`,
+ left: `${posProp.left - 5}px`,
+ 'padding-top': `${posProp['padding-top'] + 5}px`,
+ 'padding-left': `${posProp['padding-left'] + 5}px`,
+ 'padding-right': `${posProp['padding-right'] + 5}px`,
+ 'padding-bottom': `${posProp['padding-bottom'] + 5}px`,
+ 'margin-bottom': `${posProp['margin-bottom'] - 10}px`,
+ })
+ .data('quickedit-padded', true);
+ }, 0);
+ },
+
+ /**
+ * Removes the padding around the element being edited when editing ceases.
+ */
+ _unpad() {
+ // Early return if the element has not been padded.
+ if (!this.$el.data('quickedit-padded')) {
+ return;
+ }
+ const self = this;
+
+ // 1) Set the empty width again.
+ if (this._widthAttributeIsEmpty) {
+ this.$el.addClass('quickedit-animate-disable-width').css('width', '');
+ }
+
+ // 2) Remove padding; use animations (these will run simultaneously with)
+ // the fading out of the toolbar as its gets removed).
+ const posProp = this._getPositionProperties(this.$el);
+ setTimeout(() => {
+ // Re-enable width animations (padding changes affect width too!).
+ self.$el.removeClass('quickedit-animate-disable-width');
+
+ // Unpad the editable.
+ self.$el.css({
position: 'relative',
top: `${posProp.top + 5}px`,
left: `${posProp.left + 5}px`,
'padding-bottom': `${posProp['padding-bottom'] - 5}px`,
'margin-bottom': `${posProp['margin-bottom'] + 10}px`,
});
- }, 0);
- // Remove the marker that indicates that this field has padding. This is
- // done outside the timed out function above so that we don't get numerous
- // queued functions that will remove padding before the data marker has
- // been removed.
- this.$el.removeData('quickedit-padded');
+ }, 0);
+ // Remove the marker that indicates that this field has padding. This is
+ // done outside the timed out function above so that we don't get numerous
+ // queued functions that will remove padding before the data marker has
+ // been removed.
+ this.$el.removeData('quickedit-padded');
+ },
+
+ /**
+ * Gets the top and left properties of an element.
+ *
+ * Convert extraneous values and information into numbers ready for
+ * subtraction.
+ *
+ * @param {jQuery} $e
+ * The element to get position properties from.
+ *
+ * @return {object}
+ * An object containing css values for the needed properties.
+ */
+ _getPositionProperties($e) {
+ let p;
+ const r = {};
+ const props = [
+ 'top',
+ 'left',
+ 'bottom',
+ 'right',
+ 'padding-top',
+ 'padding-left',
+ 'padding-right',
+ 'padding-bottom',
+ 'margin-bottom',
+ ];
+
+ const propCount = props.length;
+ for (let i = 0; i < propCount; i++) {
+ p = props[i];
+ r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10);
+ }
+ return r;
+ },
+
+ /**
+ * Replaces blank or 'auto' CSS `position: <value>` values with "0px".
+ *
+ * @param {string} [pos]
+ * The value for a CSS position declaration.
+ *
+ * @return {string}
+ * A CSS value that is valid for `position`.
+ */
+ _replaceBlankPosition(pos) {
+ if (pos === 'auto' || !pos) {
+ pos = '0px';
+ }
+ return pos;
+ },
},
-
- /**
- * Gets the top and left properties of an element.
- *
- * Convert extraneous values and information into numbers ready for
- * subtraction.
- *
- * @param {jQuery} $e
- * The element to get position properties from.
- *
- * @return {object}
- * An object containing css values for the needed properties.
- */
- _getPositionProperties($e) {
- let p;
- const r = {};
- const props = [
- 'top', 'left', 'bottom', 'right',
- 'padding-top', 'padding-left', 'padding-right', 'padding-bottom',
- 'margin-bottom',
- ];
-
- const propCount = props.length;
- for (let i = 0; i < propCount; i++) {
- p = props[i];
- r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10);
- }
- return r;
- },
-
- /**
- * Replaces blank or 'auto' CSS `position: <value>` values with "0px".
- *
- * @param {string} [pos]
- * The value for a CSS position declaration.
- *
- * @return {string}
- * A CSS value that is valid for `position`.
- */
- _replaceBlankPosition(pos) {
- if (pos === 'auto' || !pos) {
- pos = '0px';
- }
- return pos;
- },
-
- });
-}(jQuery, Backbone, Drupal));
+ );
+})(jQuery, Backbone, Drupal);