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 }; |