Skip to content
KeyboardView.js 10.1 KiB
Newer Older
 * Backbone View providing the aural view of CKEditor keyboard UX configuration.
(function ($, Drupal, Backbone, _) {
  Drupal.ckeditor.KeyboardView = Backbone.View.extend(/** @lends Drupal.ckeditor.KeyboardView# */{
     * Backbone View for CKEditor toolbar configuration; keyboard UX.
     *
     * @constructs
     *
     * @augments Backbone.View
     */
    initialize: function () {
      // Add keyboard arrow support.
      this.$el.on('keydown.ckeditor', '.ckeditor-buttons a, .ckeditor-multiple-buttons a', this.onPressButton.bind(this));
      this.$el.on('keydown.ckeditor', '[data-drupal-ckeditor-type="group"]', this.onPressGroup.bind(this));
    },

    /**
     */
    render: function () {
    },

    /**
     * Handles keypresses on a CKEditor configuration button.
     *
     */
    onPressButton: function (event) {
      var upDownKeys = [
        38, // Up arrow.
        63232, // Safari up arrow.
        40, // Down arrow.
        63233 // Safari down arrow.
      ];
      var leftRightKeys = [
        37, // Left arrow.
        63234, // Safari left arrow.
        39, // Right arrow.
        63235 // Safari right arrow.
      ];

      // Respond to an enter key press. Prevent the bubbling of the enter key
      // press to the button group parent element.
      if (event.keyCode === 13) {
        event.stopPropagation();
      }

      // Only take action when a direction key is pressed.
      if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
        var view = this;
        var $target = $(event.currentTarget);
        var $button = $target.parent();
        var $container = $button.parent();
        var $group = $button.closest('.ckeditor-toolbar-group');
        var $row = $button.closest('.ckeditor-row');
        var containerType = $container.data('drupal-ckeditor-button-sorting');
        var $availableButtons = this.$el.find('[data-drupal-ckeditor-button-sorting="source"]');
        var $activeButtons = this.$el.find('.ckeditor-toolbar-active');
        // The current location of the button, just in case it needs to be put
        // back.
        var $originalGroup = $group;
        var dir;

        // Move available buttons between their container and the active
        // toolbar.
          // Move the button to the active toolbar configuration when the down
          // or up keys are pressed.
          if (_.indexOf([40, 63233], event.keyCode) > -1) {
            // Move the button to the first row, first button group index
            // position.
            $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
          }
        }
        else if (containerType === 'target') {
          // Move buttons between sibling buttons in a group and between groups.
          if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
            // Move left.
            var $siblings = $container.children();
            var index = $siblings.index($button);
            if (_.indexOf([37, 63234], event.keyCode) > -1) {
              // Move between sibling buttons.
              if (index > 0) {
                $button.insertBefore($container.children().eq(index - 1));
              }
              // Move between button groups and rows.
              else {
                // Move between button groups.
                $group = $container.parent().prev();
                if ($group.length > 0) {
                  $group.find('.ckeditor-toolbar-group-buttons').append($button);
                }
                // Wrap between rows.
                else {
                  $container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-group').not('.placeholder').find('.ckeditor-toolbar-group-buttons').eq(-1).append($button);
                }
              }
            }
            // Move right.
            else if (_.indexOf([39, 63235], event.keyCode) > -1) {
              // Move between sibling buttons.
              if (index < ($siblings.length - 1)) {
                $button.insertAfter($container.children().eq(index + 1));
              }
              // Move between button groups. Moving right at the end of a row
              // will create a new group.
              else {
                $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
              }
            }
          }
          // Move buttons between rows and the available button set.
          else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
            dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
            $row = $container.closest('.ckeditor-row')[dir]();
            // Move the button back into the available button set.
            if (dir === 'prev' && $row.length === 0) {
              // If this is a divider, just destroy it.
              if ($button.data('drupal-ckeditor-type') === 'separator') {
                $button
                  .off()
                  .remove();
                // Focus on the first button in the active toolbar.
                $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).children().eq(0).children().trigger('focus');
              }
              // Otherwise, move it.
              else {
                $availableButtons.prepend($button);
              }
            }
            else {
              $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
            }
          }
        }
        // Move dividers between their container and the active toolbar.
        else if (containerType === 'dividers') {
          // Move the button to the active toolbar configuration when the down
          // or up keys are pressed.
          if (_.indexOf([40, 63233], event.keyCode) > -1) {
            // Move the button to the first row, first button group index
            // position.
            $button = $button.clone(true);
            $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
            $target = $button.children();
          }
        }

        view = this;
        // Attempt to move the button to the new toolbar position.
        Drupal.ckeditor.registerButtonMove(this, $button, function (result) {

          // Put the button back if the registration failed.
          // If the button was in a row, then it was in the active toolbar
          // configuration. The button was probably placed in a new group, but
          // that action was canceled.
          if (!result && $originalGroup) {
            $originalGroup.find('.ckeditor-buttons').append($button);
          }
          // Otherwise refresh the sortables to acknowledge the new button
          // positions.
          else {
            view.$el.find('.ui-sortable').sortable('refresh');
          }
          // Refocus the target button so that the user can continue from a
          // known place.
          $target.trigger('focus');
        });

        event.preventDefault();
        event.stopPropagation();
      }
    },

    /**
     * Handles keypresses on a CKEditor configuration group.
     *
     */
    onPressGroup: function (event) {
      var upDownKeys = [
        38, // Up arrow.
        63232, // Safari up arrow.
        40, // Down arrow.
        63233 // Safari down arrow.
      ];
      var leftRightKeys = [
        37, // Left arrow.
        63234, // Safari left arrow.
        39, // Right arrow.
        63235 // Safari right arrow.
      ];

      // Respond to an enter key press.
      if (event.keyCode === 13) {
        var view = this;
        // Open the group renaming dialog in the next evaluation cycle so that
        // this event can be cancelled and the bubbling wiped out. Otherwise,
        // Firefox has issues because the page focus is shifted to the dialog
        // along with the keydown event.
        window.setTimeout(function () {
          Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
        }, 0);
        event.preventDefault();
        event.stopPropagation();
      }

      // Respond to direction key presses.
      if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
        var $group = $(event.currentTarget);
        var $container = $group.parent();
        var $siblings = $container.children();
        // Move groups between sibling groups.
        if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
          index = $siblings.index($group);
          // Move left between sibling groups.
          if ((_.indexOf([37, 63234], event.keyCode) > -1)) {
            if (index > 0) {
              $group.insertBefore($siblings.eq(index - 1));
            }
            // Wrap between rows. Insert the group before the placeholder group
            // at the end of the previous row.
            else {
              $group.insertBefore($container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-groups').children().eq(-1));
            }
          }
          // Move right between sibling groups.
          else if (_.indexOf([39, 63235], event.keyCode) > -1) {
            // Move to the right if the next group is not a placeholder.
            if (!$siblings.eq(index + 1).hasClass('placeholder')) {
              $group.insertAfter($container.children().eq(index + 1));
            }
            // Wrap group between rows.
            else {
              $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
            }
          }

        }
        // Move groups between rows.
        else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
          dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
          $group.closest('.ckeditor-row')[dir]().find('.ckeditor-toolbar-groups').eq(0).prepend($group);
        }

        Drupal.ckeditor.registerGroupMove(this, $group);
        $group.trigger('focus');
        event.preventDefault();
        event.stopPropagation();
      }
    }
  });

})(jQuery, Drupal, Backbone, _);