diff --git a/core/modules/contextual/contextual.toolbar.js b/core/modules/contextual/contextual.toolbar.js index e44b60b..c5032e6 100644 --- a/core/modules/contextual/contextual.toolbar.js +++ b/core/modules/contextual/contextual.toolbar.js @@ -16,7 +16,9 @@ */ Drupal.behaviors.contextualToolbar = { attach: function (context) { + var that = this; $('body').once('contextualToolbar-init', function () { + var options = $.extend({}, that.defaults); var $contextuals = $(context).find('.contextual-links'); var $tab = $('.js .toolbar .bar .contextual-toolbar-tab'); var model = new Drupal.contextualToolbar.models.EditToggleModel({ @@ -25,7 +27,8 @@ Drupal.behaviors.contextualToolbar = { }); var view = new Drupal.contextualToolbar.views.EditToggleView({ el: $tab, - model: model + model: model, + strings: options.strings }); // Update the model based on overlay events. @@ -54,6 +57,18 @@ Drupal.behaviors.contextualToolbar = { model.set('isViewing', false); } }); + }, + + defaults: { + strings: { + tabbingReleased: Drupal.t('Tabbing is no longer constrained by the Contextual module'), + tabbingConstrained: Drupal.t('Tabbing is constrained to a set of @contextualsCount and the Edit mode toggle'), + pressEsc: Drupal.t('Press the esc key to exit.'), + contextualsCount: { + singular: '@count contextual link', + plural: '@count contextual links' + } + } } }; @@ -83,13 +98,22 @@ Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({ events: { 'click': 'onClick' }, + // Tracks whether the tabbing constraint announcement has been read once yet. + announcedOnce: false, + /** * Implements Backbone Views' initialize(). */ initialize: function () { + + this.strings = this.options.strings; + this.model.on('change', this.render, this); this.model.on('change:isViewing', this.persist, this); this.model.on('change:isViewing', this.manageTabbing, this); + + $(document) + .on('keyup', $.proxy(this.onKeypress, this)); }, /** @@ -104,12 +128,33 @@ Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({ var isViewing = this.model.get('isViewing'); this.$el.find('button') .toggleClass('active', !isViewing) - .prop('aria-pressed', !isViewing); + .attr('aria-pressed', !isViewing); return this; }, /** + * Limits tabbing to the contextual links and edit mode toolbar tab. + * + * @param Drupal.contextualToolbar.models.EditToggleModel model + * An EditToggleModel Backbone model. + * @param bool isViewing + * The value of the isViewing attribute in the model. + */ + manageTabbing: function (model, isViewing) { + var tabbingContext = this.model.get('tabbingContext'); + // Always release an existing tabbing context. + if (tabbingContext) { + tabbingContext.release(); + } + // Create a new tabbing context when edit mode is enabled. + if (!isViewing) { + tabbingContext = Drupal.TabbingManager.constrain($('.contextual-toolbar-tab, .contextual')); + this.model.set('tabbingContext', tabbingContext); + } + }, + + /** * Model change handler; persists the isViewing value to localStorage. * * isViewing === true is the default, so only stores in localStorage when @@ -130,39 +175,54 @@ Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({ }, /** - * Limits tabbing to the contextual links and edit mode toolbar tab. - * - * @param Drupal.contextualToolbar.models.EditToggleModel model - * An EditToggleModel Backbone model. - * @param bool isViewing - * The value of the isViewing attribute in the model. + * Passes state update messsages to Drupal.announce. */ - manageTabbing: function (model, isViewing) { - var contextuals = this.model.get('contextuals'); - // Always release an existing tabbing context. - var tabbingContext = this.model.get('tabbingContext'); - if (tabbingContext) { - tabbingContext.release(); - if (isViewing) { - Drupal.announce(Drupal.t('Tabbing is no longer constrained by the Contextual module.')) - } - } - // Create a new tabbing context when edit mode is enabled. + announceTabbingConstraint: function () { + var isViewing = this.model.get('isViewing'); + if (!isViewing) { - tabbingContext = Drupal.TabbingManager.constrain($('.contextual-toolbar-tab, .contextual')); - this.model.set('tabbingContext', tabbingContext); - Drupal.announce( - Drupal.t('Tabbing is constrained to set of @contextualsCount and the Edit mode toggle.', { - '@contextualsCount': Drupal.formatPlural(contextuals.length, '@count contextual link', '@count contextual links') - }) - ); + var contextuals = this.model.get('contextuals'); + Drupal.announce(Drupal.t(this.strings.tabbingConstrained, { + '@contextualsCount': Drupal.formatPlural(contextuals.length, this.strings.contextualsCount.singular, this.strings.contextualsCount.plural) + })); + Drupal.announce(this.strings.pressEsc); + } + else { + Drupal.announce(this.strings.tabbingReleased) } }, + /** + * Responds to the edit mode toggle toolbar button; Toggles edit mode. + * + * @param jQuery.Event event + */ onClick: function (event) { this.model.set('isViewing', !this.model.get('isViewing')); + this.announceTabbingConstraint(); + this.announcedOnce = true; event.preventDefault(); event.stopPropagation(); + }, + + /** + * Responds to esc and tab key press events. + * + * @param jQuery.Event event + */ + onKeypress: function (event) { + // Respond to tab key press; Call render so the state announcement is read. + // The first tab key press is tracked so that an annoucement about tabbing + // constraints can be raised if edit mode is enabled when this page loads. + if (!this.announcedOnce && event.keyCode === 9 && !this.model.get('isViewing')) { + this.announceTabbingConstraint(); + // Set announce to true so that this conditional block won't be run again. + this.announcedOnce = true; + } + // Respond to the ESC key. Exit out of edit mode. + if (event.keyCode === 27) { + this.model.set('isViewing', true); + } } });