(function(define, undefined) { 'use strict'; define([ 'jquery', 'underscore', 'annotator_1.2.9', 'js/edxnotes/utils/utils', 'edx-ui-toolkit/js/utils/html-utils' ], function($, _, Annotator, Utils, HtmlUtils) { var _t = Annotator._t; /** * The original _setupDynamicStyle uses a very expensive call to * Util.maxZIndex(...) that sets the z-index of .annotator-adder, * .annotator-outer, .annotator-notice, .annotator-filter. We set these * values in annotator.min.css instead and do nothing here. */ Annotator.prototype._setupDynamicStyle = function() { }; Annotator.frozenSrc = null; /** * Modifies Annotator.Plugin.Auth.haveValidToken to make it work with a new * token format. **/ Annotator.Plugin.Auth.prototype.haveValidToken = function() { return ( this._unsafeToken && this._unsafeToken.sub && this._unsafeToken.exp && this._unsafeToken.iat && this.timeToExpiry() > 0 ); }; /** * Modifies Annotator.Plugin.Auth.timeToExpiry to make it work with a new * token format. **/ Annotator.Plugin.Auth.prototype.timeToExpiry = function() { var now = new Date().getTime() / 1000, expiry = this._unsafeToken.exp, timeToExpiry = expiry - now; return (timeToExpiry > 0) ? timeToExpiry : 0; }; Annotator.Plugin.Tags.prototype.updateField = _.compose( function() { // Add screen reader label for edit mode. Note that the id of the tags element will not always be "1". // It depends on the number of annotatable components on the page. var tagsField = $('li.annotator-item >input', this.annotator.editor.element).attr('id'); if ($("label.sr[for='" + tagsField + "']", this.annotator.editor.element).length === 0) { HtmlUtils.prepend( $('#' + tagsField, this.annotator.editor.element), $(HtmlUtils.joinHtml( HtmlUtils.HTML('') ))); } return this; }, Annotator.Plugin.Tags.prototype.updateField ); Annotator.Plugin.Tags.prototype.updateViewer = _.compose( function() { // Add ARIA information for viewing mode. $('div.annotator-tags', this.wrapper).attr({ role: 'region', 'aria-label': 'tags' }); return this; }, Annotator.Plugin.Tags.prototype.updateViewer ); /** * Modifies Annotator.highlightRange to add "tabindex=0" and role="link" * attributes to the markup that encloses the * note. These are then focusable via the TAB key and are accessible to * screen readers. **/ Annotator.prototype.highlightRange = _.compose( function(results) { $('.annotator-hl', this.wrapper).attr({ tabindex: 0, role: 'link' }); return results; }, Annotator.prototype.highlightRange ); /** * Modifies Annotator.destroy to unbind click.edxnotes:freeze from the * document and reset isFrozen to default value, false. **/ Annotator.prototype.destroy = _.compose( Annotator.prototype.destroy, function() { // We are destroying the instance that has the popup visible, revert to default, // unfreeze all instances and set their isFrozen to false if (this === Annotator.frozenSrc) { this.unfreezeAll(); } else { // Unfreeze only this instance and unbound associated 'click.edxnotes:freeze' handler $(document).off('click.edxnotes:freeze' + this.uid); this.isFrozen = false; } if (this.logger && this.logger.destroy) { this.logger.destroy(); } // Unbind onNoteClick from click this.viewer.element.off('click', this.onNoteClick); this.wrapper.off('click', '.annotator-hl'); } ); /** * Modifies Annotator.Viewer.html template to make viewer div focusable. * Also adds a close button and necessary i18n attributes to all buttons. **/ Annotator.Viewer.prototype.html = { element: [ '
', '', '
' ].join(''), item: [ '
  • ', '', '', _t('View as webpage'), '', '', '', '', '', '
  • ' ].join('') }; /** * Overrides Annotator._setupViewer to add a "click" event on viewer and to * improve line breaks. **/ Annotator.prototype._setupViewer = function() { var self = this; this.viewer = new Annotator.Viewer({readOnly: this.options.readOnly}); this.viewer.element.on('click', _.bind(this.onNoteClick, this)); this.viewer.hide() .on('edit', this.onEditAnnotation) .on('delete', this.onDeleteAnnotation) .addField({ load: function(field, annotation) { if (annotation.text) { $(field).html(HtmlUtils.HTML(Utils.nl2br(Annotator.Util.escape(annotation.text))).toString()); } else { // eslint-disable-next-line max-len $(field).html(HtmlUtils.joinHtml(HtmlUtils.HTML(''), _t('No Comment'), HtmlUtils.HTML('')).toString()); } return self.publish('annotationViewerTextField', [field, annotation]); } }) .element.appendTo(this.wrapper).bind({ // xss-lint: disable=javascript-jquery-insert-into-target mouseover: this.clearViewerHideTimer, mouseout: this.startViewerHideTimer }); return this; }; Annotator.Editor.prototype.isShown = Annotator.Viewer.prototype.isShown; /** * Modifies Annotator.Editor.html template to add tabindex = -1 to * form.annotator-widget and reverse order of Save and Cancel buttons. **/ Annotator.Editor.prototype.html = [ '
    ', '
    ', '
      ', '
      ', '', '', '
      ', '
      ', '
      ' ].join(''); /** * Modifies Annotator.Editor.show, in the case of a keydown event, to remove * focus from Save button and put it on form.annotator-widget instead. * * Also add a sr label for note textarea. **/ Annotator.Editor.prototype.show = _.compose( function(event) { // Add screen reader label for the note area. Note that the id of the tags element will not always be "0". // It depends on the number of annotatable components on the page. var noteField = $('li.annotator-item >textarea', this.element).attr('id'); if ($("label.sr[for='" + noteField + "']", this.element).length === 0) { HtmlUtils.prepend( $('#' + noteField, this.element), $(HtmlUtils.joinHtml( HtmlUtils.HTML('') ))); } if (event.type === 'keydown') { this.element.find('.annotator-save').removeClass(this.classes.focus); this.element.find('form.annotator-widget').focus(); } }, Annotator.Editor.prototype.show ); /** * Removes the textarea keydown event handler as it triggers 'processKeypress' * which hides the viewer on ESC and saves on ENTER. We will define different * behaviors for these in /plugins/accessibility.js **/ delete Annotator.Editor.prototype.events['textarea keydown']; /** * Modifies Annotator.onHighlightMouseover to avoid showing the viewer if the * editor is opened. **/ // xss-lint: disable=javascript-jquery-insertion Annotator.prototype.onHighlightMouseover = _.wrap( Annotator.prototype.onHighlightMouseover, function(func, event) { // Do nothing if the editor is opened. if (this.editor.isShown()) { return false; } return func.call(this, event); }, Annotator.prototype._setupViewer ); /** * Modifies Annotator._setupWrapper to add a "click" event on '.annotator-hl'. **/ Annotator.prototype._setupWrapper = _.compose( function() { this.element.on('click', '.annotator-hl', _.bind(this.onHighlightClick, this)); return this; }, Annotator.prototype._setupWrapper ); $.extend(true, Annotator.prototype, { isFrozen: false, uid: _.uniqueId(), onHighlightClick: function(event) { event.stopPropagation(); if (!this.editor.isShown()) { this.unfreezeAll(); this.onHighlightMouseover.call(this, event); Annotator.frozenSrc = this; this.freezeAll(); } }, onNoteClick: function(event) { var $target = $(event.target); event.stopPropagation(); Annotator.Util.preventEventDefault(event); if (!($target.is('.annotator-delete') || $target.is('.annotator-close'))) { Annotator.frozenSrc = this; this.freezeAll(); } else if ($target.is('.annotator-close')) { this.viewer.hide(); } }, freeze: function() { if (!this.isFrozen) { // Remove default events this.removeEvents(); this.viewer.element.unbind('mouseover mouseout'); this.uid = _.uniqueId(); $(document).on('click.edxnotes:freeze' + this.uid, _.bind(this.unfreeze, this)); this.isFrozen = true; } return this; }, unfreeze: function() { if (this.isFrozen) { // Add default events this.addEvents(); this.viewer.element.bind({ mouseover: this.clearViewerHideTimer, mouseout: this.startViewerHideTimer }); this.viewer.hide(); $(document).off('click.edxnotes:freeze' + this.uid); this.isFrozen = false; Annotator.frozenSrc = null; } return this; }, freezeAll: function() { _.invoke(Annotator._instances, 'freeze'); return this; }, unfreezeAll: function() { _.invoke(Annotator._instances, 'unfreeze'); return this; }, showFrozenViewer: function(annotations, location) { this.showViewer(annotations, location); this.freezeAll(); return this; } }); }); }).call(this, define || RequireJS.define);