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