/* eslint-disable */ (function(define, undefined) { 'use strict'; define(['jquery', 'underscore', 'annotator_1.2.9', 'edx-ui-toolkit/js/utils/constants', ], function($, _, Annotator, Constants) { /** * Adds the Accessibility Plugin **/ Annotator.Plugin.Accessibility = function() { _.bindAll(this, 'addAriaAttributes', 'onHighlightKeyDown', 'onViewerKeyDown', 'onEditorKeyDown', 'addDescriptions', 'removeDescription', 'focusOnGrabber', 'showViewer', 'onClose', 'focusOnHighlightedText' ); // Call the Annotator.Plugin constructor this sets up the element and // options properties. Annotator.Plugin.apply(this, arguments); }; $.extend(Annotator.Plugin.Accessibility.prototype, new Annotator.Plugin(), { pluginInit: function() { this.annotator.subscribe('annotationViewerTextField', this.addAriaAttributes); this.annotator.subscribe('annotationsLoaded', this.addDescriptions); this.annotator.subscribe('annotationCreated', this.addDescriptions); this.annotator.subscribe('annotationCreated', this.focusOnHighlightedText); this.annotator.subscribe('annotationDeleted', this.removeDescription); this.annotator.element.on('keydown.accessibility.hl', '.annotator-hl', this.onHighlightKeyDown); this.annotator.element.on('keydown.accessibility.viewer', '.annotator-viewer', this.onViewerKeyDown); this.annotator.element.on('keydown.accessibility.editor', '.annotator-editor', this.onEditorKeyDown); this.addFocusGrabber(); this.addTabIndex(); }, destroy: function() { this.annotator.unsubscribe('annotationViewerTextField', this.addAriaAttributes); this.annotator.unsubscribe('annotationsLoaded', this.addDescriptions); this.annotator.unsubscribe('annotationCreated', this.addDescriptions); this.annotator.unsubscribe('annotationCreated', this.focusOnHighlightedText); this.annotator.unsubscribe('annotationDeleted', this.removeDescription); this.annotator.element.off('.accessibility'); this.removeFocusGrabber(); }, addTabIndex: function() { this.annotator.element .find('.annotator-edit, .annotator-delete') .attr('tabindex', 0); }, addFocusGrabber: function() { this.focusGrabber = $('', { class: 'edx-notes-focus-grabber', tabindex: '-1' }); this.annotator.wrapper.before(this.focusGrabber); // xss-lint: disable=javascript-jquery-insertion }, removeFocusGrabber: function() { if (this.focusGrabber) { this.focusGrabber.remove(); this.focusGrabber = null; } }, focusOnGrabber: function() { this.annotator.wrapper.siblings('.edx-notes-focus-grabber').focus(); }, addDescriptions: function(annotations) { if (!_.isArray(annotations)) { annotations = [annotations]; } _.each(annotations, function(annotation) { var id = annotation.id || _.uniqueId(); this.annotator.wrapper.after($('
', { class: 'aria-note-description sr', id: 'aria-note-description-' + id, text: Annotator.Util.escape(annotation.text) // xss-lint: disable=javascript-escape })); $(annotation.highlights).attr({ 'aria-describedby': 'aria-note-description-' + id }); }, this); }, removeDescription: function(annotation) { var id = $(annotation.highlights).attr('aria-describedby'); $('#' + id).remove(); }, addAriaAttributes: function(field, annotation) { // Add ARIA attributes to associated note ie
My note
$(field).attr({ tabindex: -1, role: 'note', class: 'annotator-note' }); }, focusOnHighlightedText: function() { var viewer = this.annotator.viewer, editor = this.annotator.editor, highlight; try { if (viewer.isShown()) { highlight = viewer.annotations[0].highlights[0]; } else if (editor.isShown()) { highlight = editor.annotation.highlights[0]; } highlight.focus(); } catch (err) {} }, getViewerTabControls: function() { var viewer, note, viewerControls, editButton, delButton, closeButton, tabControls = []; // Viewer elements viewer = this.annotator.element.find('.annotator-viewer'); note = viewer.find('.annotator-note'); viewerControls = viewer.find('.annotator-controls'); editButton = viewerControls.find('.annotator-edit'); delButton = viewerControls.find('.annotator-delete'); closeButton = viewerControls.find('.annotator-close'); tabControls.push(note, editButton, delButton, closeButton); return tabControls; }, getEditorTabControls: function() { var editor, editorControls, textArea, saveButton, cancelButton, tabControls = [], annotatorItems, tagInput = null; // Editor elements editor = this.annotator.element.find('.annotator-editor'); editorControls = editor.find('.annotator-controls'); annotatorItems = editor.find('.annotator-listing').find('.annotator-item'); textArea = annotatorItems.first().children('textarea'); saveButton = editorControls.find('.annotator-save'); cancelButton = editorControls.find('.annotator-cancel'); // If the tags plugin is enabled, add the ability to tab into it. if (annotatorItems.length > 1) { tagInput = annotatorItems.first().next().children('input'); } tabControls.push(textArea); if (tagInput) { tabControls.push(tagInput); } tabControls.push(saveButton, cancelButton); return tabControls; }, focusOnNextTabControl: function(tabControls, tabControl) { var nextIndex; _.each(tabControls, function(element, index) { if (element.is(tabControl)) { nextIndex = index === tabControls.length - 1 ? 0 : index + 1; tabControls[nextIndex].focus(); } }); }, focusOnPreviousTabControl: function(tabControls, tabControl) { var previousIndex; _.each(tabControls, function(element, index) { if (element.is(tabControl)) { previousIndex = index === 0 ? tabControls.length - 1 : index - 1; tabControls[previousIndex].focus(); } }); }, showViewer: function(position, annotation) { annotation = $.makeArray(annotation); this.annotator.showViewer(annotation, position); this.annotator.element.find('.annotator-listing').focus(); this.annotator.subscribe('annotationDeleted', this.focusOnGrabber); }, onClose: function() { this.focusOnHighlightedText(); this.annotator.unsubscribe('annotationDeleted', this.focusOnGrabber); }, onHighlightKeyDown: function(event) { var key = Constants.keyCodes, keyCode = event.keyCode, $target = $(event.currentTarget), annotation, position; switch (keyCode) { case key.tab: // This happens only when coming from notes page if (this.annotator.viewer.isShown()) { this.annotator.element.find('.annotator-listing').focus(); event.preventDefault(); event.stopPropagation(); } break; case key.enter: case key.space: if (!this.annotator.viewer.isShown()) { position = $target.position(); this.showViewer(position, $target.data('annotation')); event.preventDefault(); event.stopPropagation(); } break; case key.esc: this.annotator.viewer.hide(); event.preventDefault(); event.stopPropagation(); break; } }, onViewerKeyDown: function(event) { var key = Constants.keyCodes, keyCode = event.keyCode, $target = $(event.target), listing = this.annotator.element.find('.annotator-listing'), tabControls; switch (keyCode) { case key.tab: tabControls = this.getViewerTabControls(); if (event.shiftKey) { // Tabbing backwards if ($target.is(listing)) { _.last(tabControls).focus(); } else { this.focusOnPreviousTabControl(tabControls, $target); } } else { // Tabbing forward if ($target.is(listing)) { _.first(tabControls).focus(); } else { this.focusOnNextTabControl(tabControls, $target); } } event.preventDefault(); event.stopPropagation(); break; case key.enter: case key.space: if ($target.hasClass('annotator-close')) { this.onClose(); this.annotator.viewer.hide(); event.preventDefault(); } break; case key.esc: this.onClose(); this.annotator.viewer.hide(); event.preventDefault(); break; } }, onEditorKeyDown: function(event) { var key = Constants.keyCodes, keyCode = event.keyCode, $target = $(event.target), editor, form, editorControls, save, cancel, tabControls; editor = this.annotator.element.find('.annotator-editor'); form = editor.find('.annotator-widget'); editorControls = editor.find('.annotator-controls'); save = editorControls.find('.annotator-save'); cancel = editorControls.find('.annotator-cancel'); switch (keyCode) { case key.tab: tabControls = this.getEditorTabControls(); if (event.shiftKey) { // Tabbing backwards if ($target.is(form)) { _.last(tabControls).focus(); } else { this.focusOnPreviousTabControl(tabControls, $target); } } else { // Tabbing forward if ($target.is(form)) { _.first(tabControls).focus(); } else { this.focusOnNextTabControl(tabControls, $target); } } event.preventDefault(); event.stopPropagation(); break; case key.enter: if ($target.is(save) || event.metaKey || event.ctrlKey) { this.onClose(); this.annotator.editor.submit(); } else if ($target.is(cancel)) { this.onClose(); this.annotator.editor.hide(); } else { break; } event.preventDefault(); break; case key.space: if ($target.is(save)) { this.onClose(); this.annotator.editor.submit(); } else if ($target.is(cancel)) { this.onClose(); this.annotator.editor.hide(); } else { break; } event.preventDefault(); break; case key.esc: this.onClose(); this.annotator.editor.hide(); event.preventDefault(); break; } } }); }); }).call(this, define || RequireJS.define);