X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;ds=sidebyside;f=web%2Fcore%2Fmodules%2Fquickedit%2Fjs%2Fviews%2FEntityToolbarView.js;fp=web%2Fcore%2Fmodules%2Fquickedit%2Fjs%2Fviews%2FEntityToolbarView.js;h=bc5ab75db946d8bc165cc59f2e588345b4ba8782;hb=9917807b03b64faf00f6a1f29dcb6eafc454efa5;hp=4fa0506f5382ecc27930dd50ccc77010701cba14;hpb=aea91e65e895364e460983b890e295aa5d5540a5;p=yaffs-website diff --git a/web/core/modules/quickedit/js/views/EntityToolbarView.js b/web/core/modules/quickedit/js/views/EntityToolbarView.js index 4fa0506f5..bc5ab75db 100644 --- a/web/core/modules/quickedit/js/views/EntityToolbarView.js +++ b/web/core/modules/quickedit/js/views/EntityToolbarView.js @@ -1,128 +1,75 @@ /** - * @file - * A Backbone View that provides an entity level toolbar. - */ +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ (function ($, _, Backbone, Drupal, debounce) { - - 'use strict'; - - Drupal.quickedit.EntityToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityToolbarView# */{ - - /** - * @type {jQuery} - */ + Drupal.quickedit.EntityToolbarView = Backbone.View.extend({ _fieldToolbarRoot: null, - /** - * @return {object} - * A map of events. - */ - events: function () { + events: function events() { var map = { 'click button.action-save': 'onClickSave', 'click button.action-cancel': 'onClickCancel', - 'mouseenter': 'onMouseenter' + mouseenter: 'onMouseenter' }; return map; }, - - /** - * @constructs - * - * @augments Backbone.View - * - * @param {object} options - * Options to construct the view. - * @param {Drupal.quickedit.AppModel} options.appModel - * A quickedit `AppModel` to use in the view. - */ - initialize: function (options) { + initialize: function initialize(options) { var that = this; this.appModel = options.appModel; this.$entity = $(this.model.get('el')); - // Rerender whenever the entity state changes. this.listenTo(this.model, 'change:isActive change:isDirty change:state', this.render); - // Also rerender whenever a different field is highlighted or activated. + this.listenTo(this.appModel, 'change:highlightedField change:activeField', this.render); - // Rerender when a field of the entity changes state. + this.listenTo(this.model.get('fields'), 'change:state', this.fieldStateChange); - // Reposition the entity toolbar as the viewport and the position within - // the viewport changes. $(window).on('resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit', debounce($.proxy(this.windowChangeHandler, this), 150)); - // Adjust the fence placement within which the entity toolbar may be - // positioned. $(document).on('drupalViewportOffsetChange.quickedit', function (event, offsets) { if (that.$fence) { that.$fence.css(offsets); } }); - // Set the entity toolbar DOM element as the el for this view. var $toolbar = this.buildToolbarEl(); this.setElement($toolbar); this._fieldToolbarRoot = $toolbar.find('.quickedit-toolbar-field').get(0); - // Initial render. this.render(); }, - - /** - * @inheritdoc - * - * @return {Drupal.quickedit.EntityToolbarView} - * The entity toolbar view. - */ - render: function () { + render: function render() { if (this.model.get('isActive')) { - // If the toolbar container doesn't exist, create it. var $body = $('body'); if ($body.children('#quickedit-entity-toolbar').length === 0) { $body.append(this.$el); } - // The fence will define a area on the screen that the entity toolbar - // will be position within. + if ($body.children('#quickedit-toolbar-fence').length === 0) { - this.$fence = $(Drupal.theme('quickeditEntityToolbarFence')) - .css(Drupal.displace()) - .appendTo($body); + this.$fence = $(Drupal.theme('quickeditEntityToolbarFence')).css(Drupal.displace()).appendTo($body); } - // Adds the entity title to the toolbar. + this.label(); - // Show the save and cancel buttons. this.show('ops'); - // If render is being called and the toolbar is already visible, just - // reposition it. + this.position(); } - // The save button text and state varies with the state of the entity - // model. var $button = this.$el.find('.quickedit-button.action-save'); var isDirty = this.model.get('isDirty'); - // Adjust the save button according to the state of the model. + switch (this.model.get('state')) { - // Quick editing is active, but no field is being edited. case 'opened': - // The saving throbber is not managed by AJAX system. The - // EntityToolbarView manages this visual element. - $button - .removeClass('action-saving icon-throbber icon-end') - .text(Drupal.t('Save')) - .removeAttr('disabled') - .attr('aria-hidden', !isDirty); + $button.removeClass('action-saving icon-throbber icon-end').text(Drupal.t('Save')).removeAttr('disabled').attr('aria-hidden', !isDirty); break; - // The changes to the fields of the entity are being committed. case 'committing': - $button - .addClass('action-saving icon-throbber icon-end') - .text(Drupal.t('Saving')) - .attr('disabled', 'disabled'); + $button.addClass('action-saving icon-throbber icon-end').text(Drupal.t('Saving')).attr('disabled', 'disabled'); break; default: @@ -132,41 +79,18 @@ return this; }, - - /** - * @inheritdoc - */ - remove: function () { - // Remove additional DOM elements controlled by this View. + remove: function remove() { this.$fence.remove(); - // Stop listening to additional events. $(window).off('resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit'); $(document).off('drupalViewportOffsetChange.quickedit'); Backbone.View.prototype.remove.call(this); }, - - /** - * Repositions the entity toolbar on window scroll and resize. - * - * @param {jQuery.Event} event - * The scroll or resize event. - */ - windowChangeHandler: function (event) { + windowChangeHandler: function windowChangeHandler(event) { this.position(); }, - - /** - * 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}. - */ - fieldStateChange: function (model, state) { + fieldStateChange: function fieldStateChange(model, state) { switch (state) { case 'active': this.render(); @@ -177,49 +101,34 @@ break; } }, - - /** - * Uses the jQuery.ui.position() method to position the entity toolbar. - * - * @param {HTMLElement} [element] - * The element against which the entity toolbar is positioned. - */ - position: function (element) { + position: function position(element) { clearTimeout(this.timer); var that = this; - // Vary the edge of the positioning according to the direction of language - // in the document. - var edge = (document.documentElement.dir === 'rtl') ? 'right' : 'left'; - // A time unit to wait until the entity toolbar is repositioned. + + var edge = document.documentElement.dir === 'rtl' ? 'right' : 'left'; + var delay = 0; - // Determines what check in the series of checks below should be - // evaluated. + var check = 0; - // When positioned against an active field that has padding, we should - // ignore that padding when positioning the toolbar, to not unnecessarily - // move the toolbar horizontally, which feels annoying. + var horizontalPadding = 0; - var of; - var activeField; - var highlightedField; - // There are several elements in the page that the entity toolbar might be - // positioned against. They are considered below in a priority order. + var of = void 0; + var activeField = void 0; + var highlightedField = void 0; + do { switch (check) { case 0: - // Position against a specific element. of = element; break; case 1: - // Position against a form container. activeField = Drupal.quickedit.app.model.get('activeField'); of = activeField && activeField.editorView && activeField.editorView.$formContainer && activeField.editorView.$formContainer.find('.quickedit-form'); break; case 2: - // Position against an active field. of = activeField && activeField.editorView && activeField.editorView.getEditedElement(); if (activeField && activeField.editorView && activeField.editorView.getQuickEditUISettings().padding) { horizontalPadding = 5; @@ -227,7 +136,6 @@ break; case 3: - // Position against a highlighted field. highlightedField = Drupal.quickedit.app.model.get('highlightedField'); of = highlightedField && highlightedField.editorView && highlightedField.editorView.getEditedElement(); delay = 250; @@ -237,7 +145,7 @@ var fieldModels = this.model.get('fields').models; var topMostPosition = 1000000; var topMostField = null; - // Position against the topmost field. + for (var i = 0; i < fieldModels.length; i++) { var pos = fieldModels[i].get('el').getBoundingClientRect().top; if (pos < topMostPosition) { @@ -249,280 +157,137 @@ delay = 50; break; } - // Prepare to check the next possible element to position against. + check++; } while (!of); - /** - * Refines the positioning algorithm of jquery.ui.position(). - * - * Invoked as the 'using' callback of jquery.ui.position() in - * positionToolbar(). - * - * @param {*} view - * The view the positions will be calculated from. - * @param {object} suggested - * A hash of top and left values for the position that should be set. It - * can be forwarded to .css() or .animate(). - * @param {object} info - * The position and dimensions of both the 'my' element and the 'of' - * elements, as well as calculations to their relative position. This - * object contains the following properties: - * @param {object} info.element - * A hash that contains information about the HTML element that will be - * positioned. Also known as the 'my' element. - * @param {object} info.target - * A hash that contains information about the HTML element that the - * 'my' element will be positioned against. Also known as the 'of' - * element. - */ function refinePosition(view, suggested, info) { - // Determine if the pointer should be on the top or bottom. var isBelow = suggested.top > info.target.top; info.element.element.toggleClass('quickedit-toolbar-pointer-top', isBelow); - // Don't position the toolbar past the first or last editable field if - // the entity is the target. + if (view.$entity[0] === info.target.element[0]) { - // Get the first or last field according to whether the toolbar is - // above or below the entity. - var $field = view.$entity.find('.quickedit-editable').eq((isBelow) ? -1 : 0); + var $field = view.$entity.find('.quickedit-editable').eq(isBelow ? -1 : 0); if ($field.length > 0) { - suggested.top = (isBelow) ? ($field.offset().top + $field.outerHeight(true)) : $field.offset().top - info.element.element.outerHeight(true); + suggested.top = isBelow ? $field.offset().top + $field.outerHeight(true) : $field.offset().top - info.element.element.outerHeight(true); } } - // Don't let the toolbar go outside the fence. + var fenceTop = view.$fence.offset().top; var fenceHeight = view.$fence.height(); var toolbarHeight = info.element.element.outerHeight(true); if (suggested.top < fenceTop) { suggested.top = fenceTop; - } - else if ((suggested.top + toolbarHeight) > (fenceTop + fenceHeight)) { + } else if (suggested.top + toolbarHeight > fenceTop + fenceHeight) { suggested.top = fenceTop + fenceHeight - toolbarHeight; } - // Position the toolbar. + info.element.element.css({ left: Math.floor(suggested.left), top: Math.floor(suggested.top) }); } - /** - * Calls the jquery.ui.position() method on the $el of this view. - */ function positionToolbar() { - that.$el - .position({ - my: edge + ' bottom', - // Move the toolbar 1px towards the start edge of the 'of' element, - // plus any horizontal padding that may have been added to the - // element that is being added, to prevent unwanted horizontal - // movement. - at: edge + '+' + (1 + horizontalPadding) + ' top', - of: of, - collision: 'flipfit', - using: refinePosition.bind(null, that), - within: that.$fence - }) - // Resize the toolbar to match the dimensions of the field, up to a - // maximum width that is equal to 90% of the field's width. - .css({ - 'max-width': (document.documentElement.clientWidth < 450) ? document.documentElement.clientWidth : 450, - // Set a minimum width of 240px for the entity toolbar, or the width - // of the client if it is less than 240px, so that the toolbar - // never folds up into a squashed and jumbled mess. - 'min-width': (document.documentElement.clientWidth < 240) ? document.documentElement.clientWidth : 240, - 'width': '100%' - }); + that.$el.position({ + my: edge + ' bottom', + + at: edge + '+' + (1 + horizontalPadding) + ' top', + of: of, + collision: 'flipfit', + using: refinePosition.bind(null, that), + within: that.$fence + }).css({ + 'max-width': document.documentElement.clientWidth < 450 ? document.documentElement.clientWidth : 450, + + 'min-width': document.documentElement.clientWidth < 240 ? document.documentElement.clientWidth : 240, + width: '100%' + }); } - // Uses the jQuery.ui.position() method. Use a timeout to move the toolbar - // only after the user has focused on an editable for 250ms. This prevents - // the toolbar from jumping around the screen. this.timer = setTimeout(function () { - // Render the position in the next execution cycle, so that animations - // on the field have time to process. This is not strictly speaking, a - // guarantee that all animations will be finished, but it's a simple - // way to get better positioning without too much additional code. _.defer(positionToolbar); }, delay); }, - - /** - * Set the model state to 'saving' when the save button is clicked. - * - * @param {jQuery.Event} event - * The click event. - */ - onClickSave: function (event) { + onClickSave: function onClickSave(event) { event.stopPropagation(); event.preventDefault(); - // Save the model. + this.model.set('state', 'committing'); }, - - /** - * Sets the model state to candidate when the cancel button is clicked. - * - * @param {jQuery.Event} event - * The click event. - */ - onClickCancel: function (event) { + onClickCancel: function onClickCancel(event) { event.preventDefault(); this.model.set('state', 'deactivating'); }, - - /** - * Clears the timeout that will eventually reposition the entity toolbar. - * - * Without this, it may reposition itself, away from the user's cursor! - * - * @param {jQuery.Event} event - * The mouse event. - */ - onMouseenter: function (event) { + onMouseenter: function onMouseenter(event) { clearTimeout(this.timer); }, - - /** - * Builds the entity toolbar HTML; attaches to DOM; sets starting position. - * - * @return {jQuery} - * The toolbar element. - */ - buildToolbarEl: function () { + buildToolbarEl: function buildToolbarEl() { var $toolbar = $(Drupal.theme('quickeditEntityToolbar', { id: 'quickedit-entity-toolbar' })); - $toolbar - .find('.quickedit-toolbar-entity') - // Append the "ops" toolgroup into the toolbar. - .prepend(Drupal.theme('quickeditToolgroup', { - classes: ['ops'], - buttons: [ - { - label: Drupal.t('Save'), - type: 'submit', - classes: 'action-save quickedit-button icon', - attributes: { - 'aria-hidden': true - } - }, - { - label: Drupal.t('Close'), - classes: 'action-cancel quickedit-button icon icon-close icon-only' - } - ] - })); - - // Give the toolbar a sensible starting position so that it doesn't - // animate on to the screen from a far off corner. - $toolbar - .css({ - left: this.$entity.offset().left, - top: this.$entity.offset().top - }); + $toolbar.find('.quickedit-toolbar-entity').prepend(Drupal.theme('quickeditToolgroup', { + classes: ['ops'], + buttons: [{ + label: Drupal.t('Save'), + type: 'submit', + classes: 'action-save quickedit-button icon', + attributes: { + 'aria-hidden': true + } + }, { + label: Drupal.t('Close'), + classes: 'action-cancel quickedit-button icon icon-close icon-only' + }] + })); + + $toolbar.css({ + left: this.$entity.offset().left, + top: this.$entity.offset().top + }); return $toolbar; }, - - /** - * Returns the DOM element that fields will attach their toolbars to. - * - * @return {jQuery} - * The DOM element that fields will attach their toolbars to. - */ - getToolbarRoot: function () { + getToolbarRoot: function getToolbarRoot() { return this._fieldToolbarRoot; }, - - /** - * Generates a state-dependent label for the entity toolbar. - */ - label: function () { - // The entity label. + label: function label() { var label = ''; var entityLabel = this.model.get('label'); - // Label of an active field, if it exists. var activeField = Drupal.quickedit.app.model.get('activeField'); var activeFieldLabel = activeField && activeField.get('metadata').label; - // Label of a highlighted field, if it exists. + var highlightedField = Drupal.quickedit.app.model.get('highlightedField'); var highlightedFieldLabel = highlightedField && highlightedField.get('metadata').label; - // The label is constructed in a priority order. + if (activeFieldLabel) { label = Drupal.theme('quickeditEntityToolbarLabel', { entityLabel: entityLabel, fieldLabel: activeFieldLabel }); - } - else if (highlightedFieldLabel) { + } else if (highlightedFieldLabel) { label = Drupal.theme('quickeditEntityToolbarLabel', { entityLabel: entityLabel, fieldLabel: highlightedFieldLabel }); - } - else { - // @todo Add XSS regression test coverage in https://www.drupal.org/node/2547437 + } else { label = Drupal.checkPlain(entityLabel); } - this.$el - .find('.quickedit-toolbar-label') - .html(label); + this.$el.find('.quickedit-toolbar-label').html(label); }, - - /** - * Adds classes to a toolgroup. - * - * @param {string} toolgroup - * A toolgroup name. - * @param {string} classes - * A string of space-delimited class names that will be applied to the - * wrapping element of the toolbar group. - */ - addClass: function (toolgroup, classes) { + addClass: function addClass(toolgroup, classes) { this._find(toolgroup).addClass(classes); }, - - /** - * Removes classes from a toolgroup. - * - * @param {string} toolgroup - * A toolgroup name. - * @param {string} classes - * A string of space-delimited class names that will be removed from the - * wrapping element of the toolbar group. - */ - removeClass: function (toolgroup, classes) { + removeClass: function removeClass(toolgroup, classes) { this._find(toolgroup).removeClass(classes); }, - - /** - * Finds a toolgroup. - * - * @param {string} toolgroup - * A toolgroup name. - * - * @return {jQuery} - * The toolgroup DOM element. - */ - _find: function (toolgroup) { + _find: function _find(toolgroup) { return this.$el.find('.quickedit-toolbar .quickedit-toolgroup.' + toolgroup); }, - - /** - * Shows a toolgroup. - * - * @param {string} toolgroup - * A toolgroup name. - */ - show: function (toolgroup) { + show: function show(toolgroup) { this.$el.removeClass('quickedit-animate-invisible'); } - }); - -})(jQuery, _, Backbone, Drupal, Drupal.debounce); +})(jQuery, _, Backbone, Drupal, Drupal.debounce); \ No newline at end of file