X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Fquickedit%2Fjs%2Fviews%2FFieldDecorationView.es6.js;fp=web%2Fcore%2Fmodules%2Fquickedit%2Fjs%2Fviews%2FFieldDecorationView.es6.js;h=b3d0317beb9d206bfb727bf4d6d82c084708f5c3;hp=d65cbf35f196ad0fce269b8c3c0bb3fe311d6d46;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hpb=74df008bdbb3a11eeea356744f39b802369bda3c diff --git a/web/core/modules/quickedit/js/views/FieldDecorationView.es6.js b/web/core/modules/quickedit/js/views/FieldDecorationView.es6.js index d65cbf35f..b3d0317be 100644 --- a/web/core/modules/quickedit/js/views/FieldDecorationView.es6.js +++ b/web/core/modules/quickedit/js/views/FieldDecorationView.es6.js @@ -3,293 +3,299 @@ * 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`, @@ -299,58 +305,64 @@ '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: ` 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: ` 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);