Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / ckeditor / js / views / VisualView.es6.js
diff --git a/web/core/modules/ckeditor/js/views/VisualView.es6.js b/web/core/modules/ckeditor/js/views/VisualView.es6.js
new file mode 100644 (file)
index 0000000..413e69b
--- /dev/null
@@ -0,0 +1,268 @@
+/**
+ * @file
+ * A Backbone View that provides the visual UX view of CKEditor toolbar
+ *   configuration.
+ */
+
+(function (Drupal, Backbone, $) {
+  Drupal.ckeditor.VisualView = Backbone.View.extend(/** @lends Drupal.ckeditor.VisualView# */{
+
+    events: {
+      'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
+      'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
+      'click .ckeditor-add-new-group button': 'onAddGroupButtonClick',
+    },
+
+    /**
+     * Backbone View for CKEditor toolbar configuration; visual UX.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize() {
+      this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render);
+
+      // Add a toggle for the button group names.
+      $(Drupal.theme('ckeditorButtonGroupNamesToggle'))
+        .prependTo(this.$el.find('#ckeditor-active-toolbar').parent());
+
+      this.render();
+    },
+
+    /**
+     * Render function for rendering the toolbar configuration.
+     *
+     * @param {*} model
+     *   Model used for the view.
+     * @param {string} [value]
+     *   The value that was changed.
+     * @param {object} changedAttributes
+     *   The attributes that was changed.
+     *
+     * @return {Drupal.ckeditor.VisualView}
+     *   The {@link Drupal.ckeditor.VisualView} object.
+     */
+    render(model, value, changedAttributes) {
+      this.insertPlaceholders();
+      this.applySorting();
+
+      // Toggle button group names.
+      let groupNamesVisible = this.model.get('groupNamesVisible');
+      // If a button was just placed in the active toolbar, ensure that the
+      // button group names are visible.
+      if (changedAttributes && changedAttributes.changes && changedAttributes.changes.isDirty) {
+        this.model.set({ groupNamesVisible: true }, { silent: true });
+        groupNamesVisible = true;
+      }
+      this.$el.find('[data-toolbar="active"]').toggleClass('ckeditor-group-names-are-visible', groupNamesVisible);
+      this.$el.find('.ckeditor-groupnames-toggle')
+        .text((groupNamesVisible) ? Drupal.t('Hide group names') : Drupal.t('Show group names'))
+        .attr('aria-pressed', groupNamesVisible);
+
+      return this;
+    },
+
+    /**
+     * Handles clicks to a button group name.
+     *
+     * @param {jQuery.Event} event
+     *   The click event on the button group.
+     */
+    onGroupNameClick(event) {
+      const $group = $(event.currentTarget).closest('.ckeditor-toolbar-group');
+      Drupal.ckeditor.openGroupNameDialog(this, $group);
+
+      event.stopPropagation();
+      event.preventDefault();
+    },
+
+    /**
+     * Handles clicks on the button group names toggle button.
+     *
+     * @param {jQuery.Event} event
+     *   The click event on the toggle button.
+     */
+    onGroupNamesToggleClick(event) {
+      this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible'));
+      event.preventDefault();
+    },
+
+    /**
+     * Prompts the user to provide a name for a new button group; inserts it.
+     *
+     * @param {jQuery.Event} event
+     *   The event of the button click.
+     */
+    onAddGroupButtonClick(event) {
+      /**
+       * Inserts a new button if the openGroupNameDialog function returns true.
+       *
+       * @param {bool} success
+       *   A flag that indicates if the user created a new group (true) or
+       *   canceled out of the dialog (false).
+       * @param {jQuery} $group
+       *   A jQuery DOM fragment that represents the new button group. It has
+       *   not been added to the DOM yet.
+       */
+      function insertNewGroup(success, $group) {
+        if (success) {
+          $group.appendTo($(event.currentTarget).closest('.ckeditor-row').children('.ckeditor-toolbar-groups'));
+          // Focus on the new group.
+          $group.trigger('focus');
+        }
+      }
+
+      // Pass in a DOM fragment of a placeholder group so that the new group
+      // name can be applied to it.
+      Drupal.ckeditor.openGroupNameDialog(this, $(Drupal.theme('ckeditorToolbarGroup')), insertNewGroup);
+
+      event.preventDefault();
+    },
+
+    /**
+     * Handles jQuery Sortable stop sort of a button group.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the group drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    endGroupDrag(event, ui) {
+      const view = this;
+      Drupal.ckeditor.registerGroupMove(this, ui.item, (success) => {
+        if (!success) {
+          // Cancel any sorting in the configuration area.
+          view.$el.find('.ckeditor-toolbar-configuration').find('.ui-sortable').sortable('cancel');
+        }
+      });
+    },
+
+    /**
+     * Handles jQuery Sortable start sort of a button.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the group drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    startButtonDrag(event, ui) {
+      this.$el.find('a:focus').trigger('blur');
+
+      // Show the button group names as soon as the user starts dragging.
+      this.model.set('groupNamesVisible', true);
+    },
+
+    /**
+     * Handles jQuery Sortable stop sort of a button.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the button drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    endButtonDrag(event, ui) {
+      const view = this;
+      Drupal.ckeditor.registerButtonMove(this, ui.item, (success) => {
+        if (!success) {
+          // Cancel any sorting in the configuration area.
+          view.$el.find('.ui-sortable').sortable('cancel');
+        }
+        // Refocus the target button so that the user can continue from a known
+        // place.
+        ui.item.find('a').trigger('focus');
+      });
+    },
+
+    /**
+     * Invokes jQuery.sortable() on new buttons and groups in a CKEditor config.
+     */
+    applySorting() {
+      // Make the buttons sortable.
+      this.$el.find('.ckeditor-buttons').not('.ui-sortable').sortable({
+        // Change this to .ckeditor-toolbar-group-buttons.
+        connectWith: '.ckeditor-buttons',
+        placeholder: 'ckeditor-button-placeholder',
+        forcePlaceholderSize: true,
+        tolerance: 'pointer',
+        cursor: 'move',
+        start: this.startButtonDrag.bind(this),
+        // Sorting within a sortable.
+        stop: this.endButtonDrag.bind(this),
+      }).disableSelection();
+
+      // Add the drag and drop functionality to button groups.
+      this.$el.find('.ckeditor-toolbar-groups').not('.ui-sortable').sortable({
+        connectWith: '.ckeditor-toolbar-groups',
+        cancel: '.ckeditor-add-new-group',
+        placeholder: 'ckeditor-toolbar-group-placeholder',
+        forcePlaceholderSize: true,
+        cursor: 'move',
+        stop: this.endGroupDrag.bind(this),
+      });
+
+      // Add the drag and drop functionality to buttons.
+      this.$el.find('.ckeditor-multiple-buttons li').draggable({
+        connectToSortable: '.ckeditor-toolbar-active .ckeditor-buttons',
+        helper: 'clone',
+      });
+    },
+
+    /**
+     * Wraps the invocation of methods to insert blank groups and rows.
+     */
+    insertPlaceholders() {
+      this.insertPlaceholderRow();
+      this.insertNewGroupButtons();
+    },
+
+    /**
+     * Inserts a blank row at the bottom of the CKEditor configuration.
+     */
+    insertPlaceholderRow() {
+      let $rows = this.$el.find('.ckeditor-row');
+      // Add a placeholder row. to the end of the list if one does not exist.
+      if (!$rows.eq(-1).hasClass('placeholder')) {
+        this.$el
+          .find('.ckeditor-toolbar-active')
+          .children('.ckeditor-active-toolbar-configuration')
+          .append(Drupal.theme('ckeditorRow'));
+      }
+      // Update the $rows variable to include the new row.
+      $rows = this.$el.find('.ckeditor-row');
+      // Remove blank rows except the last one.
+      const len = $rows.length;
+      $rows.filter((index, row) => {
+        // Do not remove the last row.
+        if (index + 1 === len) {
+          return false;
+        }
+        return $(row).find('.ckeditor-toolbar-group').not('.placeholder').length === 0;
+      })
+        // Then get all rows that are placeholders and remove them.
+        .remove();
+    },
+
+    /**
+     * Inserts a button in each row that will add a new CKEditor button group.
+     */
+    insertNewGroupButtons() {
+      // Insert an add group button to each row.
+      this.$el.find('.ckeditor-row').each(function () {
+        const $row = $(this);
+        const $groups = $row.find('.ckeditor-toolbar-group');
+        const $button = $row.find('.ckeditor-add-new-group');
+        if ($button.length === 0) {
+          $row.children('.ckeditor-toolbar-groups').append(Drupal.theme('ckeditorNewButtonGroup'));
+        }
+        // If a placeholder group exists, make sure it's at the end of the row.
+        else if (!$groups.eq(-1).hasClass('ckeditor-add-new-group')) {
+          $button.appendTo($row.children('.ckeditor-toolbar-groups'));
+        }
+      });
+    },
+  });
+}(Drupal, Backbone, jQuery));