src/widgets/LatestAnnotation.js
changeset 1072 ac1eacb3aa33
parent 1069 2409cb4cebaf
equal deleted inserted replaced
1071:02c04d2c8fd8 1072:ac1eacb3aa33
     1 /* Widget that displays the last annotation that was posted, optionally for current segment, optionally for a given username */
     1 /* Widget that displays the last annotation that was posted, optionally for current segment, optionally for a given username */
     2 
     2 import Mustache from "mustache";
     3 IriSP.Widgets.LatestAnnotation = function(player, config){
     3 import jQuery from "jquery";
     4     IriSP.Widgets.Widget.call(this, player, config);
     4 
     5 };
     5 import latestAnnotationStyles from "./LatestAnnotation.module.css";
     6 
     6 
     7 IriSP.Widgets.LatestAnnotation.prototype = new IriSP.Widgets.Widget();
     7 const LatestAnnotation = function (ns) {
     8 
     8   return class extends ns.Widgets.Widget {
     9 IriSP.Widgets.LatestAnnotation.prototype.defaults = {
     9     constructor(player, config) {
    10     pre_draw_callback: function(){
    10       super(player, config);
       
    11     }
       
    12 
       
    13     static defaults = {
       
    14       pre_draw_callback: function () {
    11         return this.importUsers();
    15         return this.importUsers();
    12     },
    16       },
    13     from_user: false,
    17       from_user: false,
    14     filter_by_segment: false,
    18       filter_by_segment: false,
    15     segments_annotation_type: "chap",
    19       segments_annotation_type: "chap",
    16     hide_without_segment: false,
    20       hide_without_segment: false,
    17     annotation_type: "contribution",
    21       annotation_type: "contribution",
    18     /*
    22       /*
    19      * Set to a username if you only want to display annotations from a given user
    23        * Set to a username if you only want to display annotations from a given user
    20      */
    24        */
    21     show_only_annotation_from_user: false,
    25       show_only_annotation_from_user: false,
    22     /*
    26       /*
    23      * Displays a button that copy currently displayed annotation into CreateAnnotation input field
    27        * Displays a button that copy currently displayed annotation into CreateAnnotation input field
    24      */
    28        */
    25     copy_and_edit_button: false,
    29       copy_and_edit_button: false,
    26     hide_annotations_list: false,
    30       hide_annotations_list: false,
    27     /*
    31       /*
    28      * Allows clicks on an annotation from Annotations to display the annotation content into this widget
    32        * Allows clicks on an annotation from Annotations to display the annotation content into this widget
    29      */
    33        */
    30     selectable_annotations: false,
    34       selectable_annotations: false,
    31     empty_message: false,
    35       empty_message: false,
    32     starts_hidden: false,
    36       starts_hidden: false,
    33     show_header: false,
    37       show_header: false,
    34     custom_header: false,
    38       custom_header: false,
    35     make_name_string_function: function(params){
    39       make_name_string_function: function (params) {
    36         return params.username ? params.username : "Anonymous";
    40         return params.username ? params.username : "Anonymous";
    37     }, 
    41       },
    38 };
    42     };
    39 
    43 
    40 IriSP.Widgets.LatestAnnotation.prototype.messages = {
    44     static messages =  {
    41     fr : {
    45       fr: {
    42         copy_and_edit: "Copier et Editer",
    46         copy_and_edit: "Copier et Editer",
    43         empty : "Aucune annotation à afficher",
    47         empty: "Aucune annotation à afficher",
    44         header: "Dernière annotation"
    48         header: "Dernière annotation",
    45     },
    49       },
    46     en: {
    50       en: {
    47         copy_and_edit: "Copy and Edit",
    51         copy_and_edit: "Copy and Edit",
    48         empty: "No annotation to display",
    52         empty: "No annotation to display",
    49         header: "Last annotation"
    53         header: "Last annotation",
    50     }
    54       },
    51 }
    55     };
    52 
    56 
    53 IriSP.Widgets.LatestAnnotation.prototype.template = 
    57     static template =
    54     "{{#show_header}}"
    58       "{{#show_header}}" +
    55     + "<p class='Ldt-LatestAnnotation-header'>"
    59       "<p class='Ldt-LatestAnnotation-header'>" +
    56     +     "{{#custom_header}}{{custom_header}}{{/custom_header}}"
    60       "{{#custom_header}}{{custom_header}}{{/custom_header}}" +
    57     +     "{{^custom_header}}{{l10n.header}}{{/custom_header}}"
    61       "{{^custom_header}}{{l10n.header}}{{/custom_header}}" +
    58     + "</p>"
    62       "</p>" +
    59     + "{{/show_header}}"
    63       "{{/show_header}}" +
    60     + "<div class='Ldt-LatestAnnotation'>"
    64       "<div class='Ldt-LatestAnnotation'>" +
    61     + "</div>";
    65       "</div>";
    62 
    66 
    63 IriSP.Widgets.LatestAnnotation.prototype.annotationTemplate =
    67     static annotationTemplate =
    64     "<div class='Ldt-LatestAnnotation-Box'>"
    68       "<div class='Ldt-LatestAnnotation-Box'>" +
    65     + "{{#copy_and_edit_button}}<div class='Ldt-LatestAnnotation-CopyEditButton'>{{button_text}}</div>{{/copy_and_edit_button}}"
    69       "{{#copy_and_edit_button}}<div class='Ldt-LatestAnnotation-CopyEditButton'>{{button_text}}</div>{{/copy_and_edit_button}}" +
    66     +     "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-CreationDate'>{{{annotation_created}}}</div>" 
    70       "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-CreationDate'>{{{annotation_created}}}</div>" +
    67     +     "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-Title'>{{{annotation_creator}}}{{#annotation_title}}: {{{annotation_title}}}{{/annotation_title}}</div>" 
    71       "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-Title'>{{{annotation_creator}}}{{#annotation_title}}: {{{annotation_title}}}{{/annotation_title}}</div>" +
    68     +     "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-Content'>"
    72       "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-Content'>" +
    69     +         "{{{annotation_content}}}"
    73       "{{{annotation_content}}}" +
    70     +     "</div>"
    74       "</div>" +
    71     + "</div>"
    75       "</div>";
    72 
    76 
    73 
    77     importUsers() {
    74 IriSP.Widgets.LatestAnnotation.prototype.importUsers = function(){
    78       if (!this.source.users_data) {
    75     if (!this.source.users_data){
       
    76         this.usernames = Array();
    79         this.usernames = Array();
    77         var _this = this,
    80         var _this = this,
    78             _list = this.getWidgetAnnotations(),
    81           _list = this.getWidgetAnnotations(),
    79             usernames_list_string = "";
    82           usernames_list_string = "";
    80         
    83 
    81         _list.forEach(function(_annotation){
    84         _list.forEach(function (_annotation) {
    82             if(_this.usernames.indexOf(_annotation.creator) == -1){
    85           if (_this.usernames.indexOf(_annotation.creator) == -1) {
    83                 _this.usernames.push(_annotation.creator);
    86             _this.usernames.push(_annotation.creator);
    84             }
    87           }
    85         });
    88         });
    86         this.usernames.forEach(function(_username){
    89         this.usernames.forEach(function (_username) {
    87             usernames_list_string+=_username+","
    90           usernames_list_string += _username + ",";
    88         })
    91         });
    89         usernames_list_string = usernames_list_string.substring(0, usernames_list_string.length - 1);
    92         usernames_list_string = usernames_list_string.substring(
    90         _url = Mustache.to_html(this.api_users_endpoint, {usernames_list_string: encodeURIComponent(usernames_list_string), usernames_list_length: this.usernames.length});
    93           0,
    91         return IriSP.jQuery.ajax({
    94           usernames_list_string.length - 1
    92             async: false,
    95         );
    93             url: _url,
    96         _url = Mustache.render(this.api_users_endpoint, {
    94             type: "GET",
    97           usernames_list_string: encodeURIComponent(usernames_list_string),
    95             success: function(_data) {
    98           usernames_list_length: this.usernames.length,
    96                 _this.source.users_data = _data.objects
    99         });
    97             },
   100         return jQuery.ajax({
    98             error: function(_xhr, _error, _thrown) {
   101           async: false,
    99                 console.log(_xhr)
   102           url: _url,
   100                 console.log(_error)
   103           type: "GET",
   101                 console.log(_thrown)
   104           success: function (_data) {
   102             }
   105             _this.source.users_data = _data.objects;
   103         })
   106           },
   104     }
   107           error: function (_xhr, _error, _thrown) {
   105 }
   108             console.log(_xhr);
   106     
   109             console.log(_error);
   107 IriSP.Widgets.LatestAnnotation.prototype.draw = function(){
   110             console.log(_thrown);
   108     var _this = this;
   111           },
   109     this.renderTemplate();
   112         });
   110     
   113       }
   111     this.annotationContainer_$ = this.$.find('.Ldt-LatestAnnotation');
   114     }
   112     
   115 
   113     if (this.selectable_annotations){
   116     draw() {
   114         this.onMdpEvent("AnnotationsList.refresh", function(){
   117       var _this = this;
   115             _this.getWidgetAnnotations().forEach(function(_annotation){
   118       this.renderTemplate();
   116                 _annotation.off("click");
   119 
   117                 _annotation.on("click", function(){
   120       this.annotationContainer_$ = this.$.find(".Ldt-LatestAnnotation");
   118                     var _user = {},
   121 
   119                         _user_display_string = "",
   122       if (this.selectable_annotations) {
   120                         _users = this.source.users_data.filter(function(_user_data){
   123         this.onMdpEvent("AnnotationsList.refresh", function () {
   121                             return _user_data.username == _annotation.creator
   124           _this.getWidgetAnnotations().forEach(function (_annotation) {
   122                         });
   125             _annotation.off("click");
   123                     if (_users.length == 0){
   126             _annotation.on("click", function () {
   124                         _user.username = _annotation.creator;
   127               var _user = {},
   125                     }
   128                 _user_display_string = "",
   126                     else {
   129                 _users = this.source.users_data.filter(function (_user_data) {
   127                         _user = _users[0];
   130                   return _user_data.username == _annotation.creator;
   128                     }
       
   129                     _user_display_string = _this.make_name_string_function(_user)
       
   130                     _html = Mustache.to_html(_this.annotationTemplate, {
       
   131                         annotation_created: _annotation.created.toLocaleDateString()+", "+_annotation.created.toLocaleTimeString(),
       
   132                         annotation_creator: _user_display_string,
       
   133                         annotation_title: _annotation.title,
       
   134                         annotation_content: _annotation.description,
       
   135                         copy_and_edit_button: _this.copy_and_edit_button,
       
   136                         button_text: _this.l10n.copy_and_edit,
       
   137                     });
       
   138                     _this.annotationContainer_$.html(_html);
       
   139                     _this.selectedAnnotation = true;
       
   140                 });
   131                 });
   141             }); 
   132               if (_users.length == 0) {
   142         });
   133                 _user.username = _annotation.creator;
   143         
   134               } else {
   144         this.segments = _this.source.getAnnotationsByTypeTitle(this.segments_annotation_type)
   135                 _user = _users[0];
   145         this.segments.forEach(function(_segment){
   136               }
   146             _segment.on("click", function(){
   137               _user_display_string = _this.make_name_string_function(_user);
   147                 _this.selectedAnnotation = false;
   138               _html = Mustache.render(_this.annotationTemplate, {
   148             })
   139                 annotation_created:
   149         })
   140                   _annotation.created.toLocaleDateString() +
       
   141                   ", " +
       
   142                   _annotation.created.toLocaleTimeString(),
       
   143                 annotation_creator: _user_display_string,
       
   144                 annotation_title: _annotation.title,
       
   145                 annotation_content: _annotation.description,
       
   146                 copy_and_edit_button: _this.copy_and_edit_button,
       
   147                 button_text: _this.l10n.copy_and_edit,
       
   148               });
       
   149               _this.annotationContainer_$.html(_html);
       
   150               _this.selectedAnnotation = true;
       
   151             });
       
   152           });
       
   153         });
       
   154 
       
   155         this.segments = _this.source.getAnnotationsByTypeTitle(
       
   156           this.segments_annotation_type
       
   157         );
       
   158         this.segments.forEach(function (_segment) {
       
   159           _segment.on("click", function () {
       
   160             _this.selectedAnnotation = false;
       
   161           });
       
   162         });
   150         this.currentSegment = false;
   163         this.currentSegment = false;
   151     }
   164       }
   152     
   165 
   153     this.onMediaEvent("timeupdate", function(){
   166       this.onMediaEvent("timeupdate", function () {
   154         _this.refresh();
   167         _this.refresh();
   155     });
   168       });
   156     this.onMediaEvent("settimerange", function(_timeRange){
   169       this.onMediaEvent("settimerange", function (_timeRange) {
   157         _this.refresh(_timeRange);
   170         _this.refresh(_timeRange);
   158     })
   171       });
   159     
   172 
   160     if (this.starts_hidden){
   173       if (this.starts_hidden) {
   161         this.visible = true;
   174         this.visible = true;
   162         this.hide();
   175         this.hide();
   163     }
   176       } else {
   164     else{
       
   165         this.visible = false;
   177         this.visible = false;
   166         this.show();
   178         this.show();
   167     }
   179       }
   168     
   180 
   169     this.selectedAnnotation = false; // This flag tells the widget if it must display last annotation (false) or clicked annotation (true)
   181       this.selectedAnnotation = false; // This flag tells the widget if it must display last annotation (false) or clicked annotation (true)
   170     this.player.trigger("AnnotationsList.refresh");
   182       this.player.trigger("AnnotationsList.refresh");
   171     this.refresh();
   183       this.refresh();
   172 }
   184     }
   173 
   185 
   174 
   186     refresh(_timeRange) {
   175 IriSP.Widgets.LatestAnnotation.prototype.refresh = function(_timeRange){
   187       _timeRange = typeof _timeRange !== "undefined" ? _timeRange : false;
   176     _timeRange = typeof _timeRange !== 'undefined' ? _timeRange : false ;
   188       var _this = this;
   177     var _this = this;
   189       if (this.hide_without_segment) {
   178     if (this.hide_without_segment){
   190         if (!_timeRange && !this.media.getTimeRange()) {
   179         if (!_timeRange && !this.media.getTimeRange()){
   191           var _currentTime = this.media.getCurrentTime();
   180             var _currentTime = this.media.getCurrentTime();
   192           var _currentSegments = this.segments.filter(function (_segment) {
   181             var _currentSegments = this.segments.filter(function(_segment){
   193             return (
   182                 return (_currentTime >= _segment.begin && _currentTime <= _segment.end)
   194               _currentTime >= _segment.begin && _currentTime <= _segment.end
       
   195             );
       
   196           });
       
   197           if (_currentSegments.length == 0) {
       
   198             this.currentSegment = false;
       
   199             this.selectedAnnotation = false;
       
   200           } else {
       
   201             this.currentSegment = _currentSegments[0];
       
   202           }
       
   203         } else {
       
   204           var _segmentBegin = _timeRange
       
   205               ? _timeRange[0]
       
   206               : this.media.getTimeRange()[0],
       
   207             _segmentEnd = _timeRange
       
   208               ? _timeRange[1]
       
   209               : this.media.getTimeRange()[1];
       
   210           if (
       
   211             !this.currentSegment ||
       
   212             this.currentSegment.begin != _segmentBegin ||
       
   213             this.currentSegment.end != _segmentEnd
       
   214           ) {
       
   215             var _currentSegments = this.segments.filter(function (_segment) {
       
   216               return (
       
   217                 _segment.begin == _segmentBegin && _segment.end == _segmentEnd
       
   218               );
   183             });
   219             });
   184             if (_currentSegments.length == 0){
   220             if (_currentSegments.length > 0) {
   185                 this.currentSegment = false;
   221               this.selectedAnnotation = false;
   186                 this.selectedAnnotation = false;
   222               this.currentSegment = _currentSegments[0];
   187             }
   223             }
   188             else {
   224           }
   189                 this.currentSegment = _currentSegments[0]
       
   190             }
       
   191         }
   225         }
   192         else {
   226         if (!this.currentSegment) {
   193             var _segmentBegin = _timeRange? _timeRange[0] : this.media.getTimeRange()[0],
   227           if (this.visible) {
   194                 _segmentEnd = _timeRange? _timeRange[1] : this.media.getTimeRange()[1];
   228             this.hide();
   195             if ((!this.currentSegment)||(this.currentSegment.begin != _segmentBegin || this.currentSegment.end != _segmentEnd)) {
   229           }
   196                 var _currentSegments = this.segments.filter(function(_segment){
   230         } else {
   197                     return _segment.begin == _segmentBegin && _segment.end == _segmentEnd
   231           if (!this.visible) {
   198                 });
   232             this.show();
   199                 if (_currentSegments.length > 0){
   233           }
   200                     this.selectedAnnotation = false;
       
   201                     this.currentSegment = _currentSegments[0];
       
   202                 }
       
   203             }
       
   204         }
   234         }
   205         if (!this.currentSegment){
   235       }
   206             if (this.visible){
   236 
   207                 this.hide();
   237       if (this.visible && !this.selectedAnnotation) {
   208             }
   238         var _list = this.getWidgetAnnotations();
       
   239 
       
   240         if (this.filter_by_segment) {
       
   241           if (!this.currentSegment) {
       
   242             _list = _list.filter(function (_annotation) {
       
   243               return false;
       
   244             });
       
   245           } else {
       
   246             _list = _list.filter(function (_annotation) {
       
   247               _annotationTime = (_annotation.begin + _annotation.end) / 2;
       
   248               return (
       
   249                 _this.currentSegment.begin <= _annotationTime &&
       
   250                 _this.currentSegment.end >= _annotationTime
       
   251               );
       
   252             });
       
   253           }
   209         }
   254         }
   210         else {
   255         _list = _list.sortBy(function (_annotation) {
   211             if (!this.visible){
   256           return _annotation.created;
   212                 this.show();
   257         });
   213             }
   258 
       
   259         var _latestAnnotation = false,
       
   260           _html = "",
       
   261           _user_display_string = "",
       
   262           _user = {};
       
   263         if (_list.length != 0) {
       
   264           _latestAnnotation = _list.pop();
       
   265           _users = this.source.users_data.filter(function (_user_data) {
       
   266             return _user_data.username == _latestAnnotation.creator;
       
   267           });
       
   268           if (_users.length == 0) {
       
   269             _user.username = _latestAnnotation.creator;
       
   270           } else {
       
   271             _user = _users[0];
       
   272           }
       
   273           _user_display_string = this.make_name_string_function(_user);
       
   274           _html = Mustache.render(this.annotationTemplate, {
       
   275             annotation_created:
       
   276               _latestAnnotation.created.toLocaleDateString() +
       
   277               ", " +
       
   278               _latestAnnotation.created.toLocaleTimeString(),
       
   279             annotation_creator: _user_display_string,
       
   280             annotation_title: _latestAnnotation.title,
       
   281             annotation_content: _latestAnnotation.description,
       
   282             copy_and_edit_button: this.copy_and_edit_button,
       
   283             button_text: this.l10n.copy_and_edit,
       
   284           });
       
   285         } else {
       
   286           var _empty_message = this.l10n.empty;
       
   287           if (this.empty_message) {
       
   288             _empty_message = this.empty_message;
       
   289           }
       
   290           _html =
       
   291             "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-NoAnnotation'>" +
       
   292             _empty_message +
       
   293             "</div>";
   214         }
   294         }
   215     }
   295         this.annotationContainer_$.html(_html);
   216     
   296       }
   217     if (this.visible && !this.selectedAnnotation){
   297 
   218         var _list = this.getWidgetAnnotations();
   298       if (this.copy_and_edit_button) {
   219         
   299         this.copyAndEditButton_$ = this.$.find(
   220         if(this.filter_by_segment){
   300           ".Ldt-LatestAnnotation-CopyEditButton"
   221             if (!this.currentSegment) {
   301         );
   222                 _list = _list.filter(function(_annotation){
       
   223                     return false;
       
   224                 });
       
   225             }
       
   226             else {
       
   227                 _list = _list.filter(function(_annotation){
       
   228                     _annotationTime = (_annotation.begin+_annotation.end)/2;
       
   229                     return (_this.currentSegment.begin <= _annotationTime && _this.currentSegment.end >= _annotationTime);
       
   230                 });
       
   231             }
       
   232         }
       
   233         _list = _list.sortBy(function(_annotation){
       
   234             return _annotation.created;
       
   235         });
       
   236         
       
   237         var _latestAnnotation = false,
       
   238             _html="",
       
   239             _user_display_string = "",
       
   240             _user = {};
       
   241         if (_list.length != 0){
       
   242             _latestAnnotation = _list.pop();
       
   243             _users = this.source.users_data.filter(function(_user_data){
       
   244                 return _user_data.username == _latestAnnotation.creator
       
   245             })
       
   246             if (_users.length == 0){
       
   247                 _user.username = _latestAnnotation.creator;
       
   248             }
       
   249             else {
       
   250                 _user = _users[0];
       
   251             }
       
   252             _user_display_string = this.make_name_string_function(_user)
       
   253             _html = Mustache.to_html(this.annotationTemplate, {
       
   254                 annotation_created: _latestAnnotation.created.toLocaleDateString()+", "+_latestAnnotation.created.toLocaleTimeString(),
       
   255                 annotation_creator: _user_display_string,
       
   256                 annotation_title: _latestAnnotation.title,
       
   257                 annotation_content: _latestAnnotation.description,
       
   258                 copy_and_edit_button: this.copy_and_edit_button,
       
   259                 button_text: this.l10n.copy_and_edit,
       
   260             });
       
   261         }
       
   262         else {
       
   263             var _empty_message = this.l10n.empty
       
   264             if (this.empty_message) {
       
   265                 _empty_message = this.empty_message
       
   266             }
       
   267             _html = "<div class='Ldt-LatestAnnotation-Element Ldt-LatestAnnotation-NoAnnotation'>"+_empty_message+"</div>";
       
   268         }
       
   269         this.annotationContainer_$.html(_html);    
       
   270     }
       
   271 
       
   272     if(this.copy_and_edit_button){
       
   273         this.copyAndEditButton_$ = this.$.find('.Ldt-LatestAnnotation-CopyEditButton');
       
   274         this.copyAndEditButton_$.click(this.functionWrapper("copy_and_edit"));
   302         this.copyAndEditButton_$.click(this.functionWrapper("copy_and_edit"));
   275     }
   303       }
   276 }
   304     }
   277 
   305 
   278 IriSP.Widgets.LatestAnnotation.prototype.copy_and_edit = function(){
   306     copy_and_edit() {
   279     this.player.trigger("CreateAnnotation.show");
   307       this.player.trigger("CreateAnnotation.show");
   280     if (this.hide_annotations_list){
   308       if (this.hide_annotations_list) {
   281         this.player.trigger("AnnotationsList.hide");
   309         this.player.trigger("AnnotationsList.hide");
   282     }
   310       }
   283     annotationText = $('.Ldt-LatestAnnotation-Content').get(0).innerHTML;
   311       annotationText = $(".Ldt-LatestAnnotation-Content").get(0).innerHTML;
   284     
   312 
   285     $('.Ldt-CreateAnnotation-Description').removeClass('empty');
   313       $(".Ldt-CreateAnnotation-Description").removeClass("empty");
   286     $('.Ldt-CreateAnnotation-Description').val(annotationText);
   314       $(".Ldt-CreateAnnotation-Description").val(annotationText);
   287 }
   315     }
   288 
   316 
   289 IriSP.Widgets.LatestAnnotation.prototype.hide = function() {
   317     hide() {
   290     if (this.visible){
   318       if (this.visible) {
   291         this.visible = false;
   319         this.visible = false;
   292         this.$.find('.Ldt-LatestAnnotation-header').hide();
   320         this.$.find(".Ldt-LatestAnnotation-header").hide();
   293         this.annotationContainer_$.hide()
   321         this.annotationContainer_$.hide();
   294     }
   322       }
   295 }
   323     }
   296 
   324 
   297 IriSP.Widgets.LatestAnnotation.prototype.show = function() {
   325     show() {
   298     if(!this.visible){
   326       if (!this.visible) {
   299         this.visible = true;
   327         this.visible = true;
   300         this.$.find('.Ldt-LatestAnnotation-header').show();
   328         this.$.find(".Ldt-LatestAnnotation-header").show();
   301         this.annotationContainer_$.show()
   329         this.annotationContainer_$.show();
   302     }
   330       }
   303 }
   331     }
       
   332   };
       
   333 };
       
   334 
       
   335 export { LatestAnnotation, latestAnnotationStyles };