Merge pull request #5603 from lduarte1991/lduarte-harvardx-pr32
Image Annotation Tool: Border and tag-based coloring
This commit is contained in:
@@ -47,3 +47,8 @@ div.mce-tinymce.mce-container.mce-panel {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QkBBB07nraNoQAAAhZJREFUKM+NkstrE1EUxr+5c08ykztpJtVoazHBF8FgQQzonyBKEZS6FrQKLl0EXBRT0ZULJSs3oii4TyHgu90IlTaL6qouWlv7Ck1N0BSnmZk714WbPHz07M4534+Pw3eAHdTY8A9+Nd9/bshU1DpnO4HXjh2ZY2J9/OSTxHTrnP8PvJYf+BDQ6qEDaQBB43jrTusUFy4oPjsYWYzF+VS91nxLYfdhKgONaQT3W/KMxr1XY5e+qj86f8zsKYYsZ6AvjWFzA8ORHkAnwN8So7evzL/8pzMAXL/Hq8mMv1up371T7Z+/c3n9cKeuDS6Xy6dN07zLuZ56Onk2Ed2/ANJsnE/PQMpgyffle+kYzwazB1+3waVS6X48Hr9BRPB9H57nYXplFKeSt8D1Hriug9XKF0x+Lmw+ys8m2m42DOOn4zhQSsGyLOi6jqONm9isbmFVFlDbaGKx8QaB1rvdlbNhGLAsC0IIGIYBIQSy2ROQ0oOp7wOPraHXEugRvDtnzjmi0SiICEIIEBGklAB9B6cmbG0AUnrY5m73h+m6DsYYTNMEYwxEBMY0hGNVhHkcZigBO9qHlDHS7cwYg23bAIBQKAQigud7IH0XwtxDoHwEIQ9SLKx0wa7rPiaivYyxESklXNeFBg0mjyNQTQSuATMSm6ipuYt//eVcLhdeXl5+UKlUlur1upqamVAv3j3/VCyOD3VqfwF6uLp3q+vMcgAAAABJRU5ErkJggg==');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* Fixes conflicting design between tinymce css and annotator css */
|
||||
.mce-ico.mce-i-resize {
|
||||
font-family: 'tinymce';
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
}
|
||||
|
||||
// if the colored highlights by tags plugin it is notified to colorize
|
||||
annotator.publish('colorizeHighlight', [an]);
|
||||
annotator.publish('externalCallToHighlightTags', [an]);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -231,7 +231,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
var span = document.createElement('span');
|
||||
var rectPosition = an.rangePosition;
|
||||
span.className = "annotator-hl";
|
||||
span.style.border = '2px solid rgba(0,0,0,0.5)';
|
||||
|
||||
// outline and border below create a double line one black and one white
|
||||
// so to be able to differentiate when selecting dark or light images
|
||||
span.style.border = '2px solid rgb(255, 255, 255)';
|
||||
span.style.outline = '2px solid rgb(0, 0, 0)';
|
||||
span.style.background = 'rgba(0,0,0,0)';
|
||||
|
||||
// Adds listening items for the viewer and editor
|
||||
@@ -305,7 +309,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
viewer.innerTracker.setTracking(false);
|
||||
this.rect = document.createElement('div');
|
||||
this.rect.style.background = 'rgba(0,0,0,0)';
|
||||
this.rect.style.border = '2px solid rgba(0,0,0,0.5)';
|
||||
|
||||
// outline and border below create a double line one black and one white
|
||||
// so to be able to differentiate when selecting dark or light images
|
||||
this.rect.style.border = '2px solid rgb(255, 255, 255)';
|
||||
this.rect.style.outline = '2px solid rgb(0, 0, 0)';
|
||||
|
||||
this.rect.style.position = 'absolute';
|
||||
this.rect.className = 'DrawingRect';
|
||||
// set the initial position
|
||||
@@ -1026,6 +1035,10 @@ OpenSeadragonAnnotation = function (element, options) {
|
||||
function reloadEditor(){
|
||||
tinymce.EditorManager.execCommand('mceRemoveEditor',true, "annotator-field-0");
|
||||
tinymce.EditorManager.execCommand('mceAddEditor',true, "annotator-field-0");
|
||||
|
||||
// if person hits into/out of fullscreen before closing the editor should close itself
|
||||
// ideally we would want to keep it open and reposition, this would make a great TODO in the future
|
||||
annotator.editor.hide();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
@@ -1044,6 +1057,14 @@ OpenSeadragonAnnotation = function (element, options) {
|
||||
document.addEventListener("msfullscreenchange", function () {
|
||||
reloadEditor();
|
||||
}, false);
|
||||
|
||||
// for some reason the above doesn't work when person hits ESC to exit full screen...
|
||||
$(document).keyup(function(e) {
|
||||
// esc key reloads editor as well
|
||||
if (e.keyCode == 27) {
|
||||
reloadEditor();
|
||||
}
|
||||
});
|
||||
|
||||
this.options = options;
|
||||
|
||||
|
||||
12
common/static/js/vendor/ova/catch/js/catch.js
vendored
12
common/static/js/vendor/ova/catch/js/catch.js
vendored
@@ -504,6 +504,13 @@ CatchAnnotation.prototype = {
|
||||
|
||||
// Search Button
|
||||
el.on("click", ".searchbox .search-icon", onSearchButtonClick);
|
||||
// Search should also run when user hits ENTER
|
||||
$('input[name=search]').keyup(function(e) {
|
||||
// ENTER == 13
|
||||
if(e.which == 13) {
|
||||
onSearchButtonClick();
|
||||
}
|
||||
});
|
||||
|
||||
// Clear Search Button
|
||||
el.on("click", ".searchbox .clear-search-icon", onClearSearchButtonClick);
|
||||
@@ -1001,6 +1008,9 @@ CatchAnnotation.prototype = {
|
||||
var positionAnnotator = videojs.findPosition(wrapper[0]);
|
||||
var positionAdder = {};
|
||||
|
||||
// the following addition to display makes sure the editor shows up
|
||||
// after opening TinyMCE/editor within the image source
|
||||
positionAdder.display = "block";
|
||||
positionAdder.left = positionLeft.left - positionAnnotator.left;
|
||||
positionAdder.top = positionLeft.top + 20 - positionAnnotator.top;
|
||||
|
||||
@@ -1010,9 +1020,9 @@ CatchAnnotation.prototype = {
|
||||
this.annotator.onAdderClick();
|
||||
|
||||
// Set vertical editor
|
||||
$(this.annotator.editor.element).css(positionAdder);
|
||||
this.annotator.editor.resetOrientation();
|
||||
this.annotator.editor.invertY();
|
||||
this.annotator.editor.element.find('.annotator-widget').css('min-width', replyElem.css('width'));
|
||||
|
||||
// set parent
|
||||
var parentValue = $(this.annotator.editor.element).find(".reply-item span.parent-annotation");
|
||||
|
||||
26
common/static/js/vendor/ova/reply-annotator.js
vendored
26
common/static/js/vendor/ova/reply-annotator.js
vendored
@@ -64,14 +64,24 @@ Annotator.Plugin.Reply = (function(_super) {
|
||||
|
||||
// New JSON for the database
|
||||
Reply.prototype.pluginSubmit = function(field, annotation) {
|
||||
var replyItem = $(this.annotator.editor.element).find(".reply-item span.parent-annotation"),
|
||||
parent = replyItem.html()!=''?replyItem.html():'0';
|
||||
console.log(parent);
|
||||
console.log(replyItem.html());
|
||||
if (parent!='0') annotation.media = 'comment';
|
||||
annotation.parent = parent;//set 0, because it is not a reply
|
||||
console.log(annotation.parent);
|
||||
return annotation.parent;
|
||||
// since each annotation has their own reply item, this "find" is element specific
|
||||
var replyItem = $(this.annotator.editor.element).find(".reply-item span.parent-annotation");
|
||||
// checks to see if the current annotation is a reply by checking parent numbers
|
||||
var parent = replyItem.html() !== '' ? replyItem.html() : '0';
|
||||
// if the parent number is not empty or zero, we know that this is a comment
|
||||
if (parent !== '0') {
|
||||
annotation.media = 'comment';
|
||||
}
|
||||
|
||||
// apparently some browsers continue adding <font> tags here for nonreplies
|
||||
// this will check and set to 0 (nonreply) if it fails
|
||||
if (parseInt(parent, 10) === NaN){
|
||||
parent = '0';
|
||||
}
|
||||
|
||||
// set 0, because it is not a reply
|
||||
annotation.parent = parent;
|
||||
return annotation.parent;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +93,17 @@ Annotator.Plugin.RichText = (function(_super) {
|
||||
// set the modification in the textarea of annotator
|
||||
$(editor.element).find('textarea')[0].value = tinymce.activeEditor.getContent();
|
||||
});
|
||||
|
||||
// creates a function called whenever editor is resized
|
||||
ed.on('init', function(mceInstance) {
|
||||
|
||||
// get win means this event activates when window is resized
|
||||
tinymce.dom.Event.bind(ed.getWin(), 'resize', function(e){
|
||||
|
||||
// mceInstance.target gets the editor, its id is used to retrieved iframe
|
||||
$("#"+mceInstance.target.id+"_ifr").css('min-width', '400px');
|
||||
});
|
||||
});
|
||||
// new button to add Rubrics of the url https://gteavirtual.org/rubric
|
||||
ed.addButton('rubric', {
|
||||
icon: 'rubric',
|
||||
|
||||
212
common/static/js/vendor/ova/tags-annotator.js
vendored
212
common/static/js/vendor/ova/tags-annotator.js
vendored
@@ -293,6 +293,8 @@ $.TokenList = function (input, url_or_data, settings) {
|
||||
case KEY.COMMA:
|
||||
if(selected_dropdown_item) {
|
||||
add_token($(selected_dropdown_item).data("tokeninput"));
|
||||
// this allows for tags to be color-coded based on instructor set-up
|
||||
annotator.publish("colorEditorTags")
|
||||
hidden_input.change();
|
||||
return false;
|
||||
} else{
|
||||
@@ -903,6 +905,7 @@ Annotator.Plugin.HighlightTags = (function(_super) {
|
||||
this.colorize = __bind(this.colorize, this);
|
||||
this.updateField = __bind(this.updateField, this);
|
||||
this.externalCall = __bind(this.externalCall, this);
|
||||
this.colorizeEditorTags = __bind(this.colorizeEditorTags, this);
|
||||
|
||||
this.options = options;
|
||||
_ref = HighlightTags.__super__.constructor.apply(this, arguments);
|
||||
@@ -947,12 +950,14 @@ Annotator.Plugin.HighlightTags = (function(_super) {
|
||||
|
||||
this.colors = this.getHighlightTags();
|
||||
var self = this;
|
||||
this.annotator.subscribe('annotationsLoaded', function(){setTimeout(function(){self.colorize()},1000)});
|
||||
this.annotator.subscribe('annotationUpdated', this.colorize);
|
||||
this.annotator.subscribe('flaggedAnnotation', this.updateViewer);
|
||||
this.annotator.subscribe('annotationCreated', this.colorize);
|
||||
this.annotator.subscribe('externalCallToHighlightTags', this.externalCall);
|
||||
|
||||
// all of these need time for the annotations database to respond
|
||||
this.annotator.subscribe('annotationsLoaded', function(){setTimeout(function(){self.colorize()}, 1000)});
|
||||
this.annotator.subscribe('annotationUpdated', function(){setTimeout(function(){self.colorize()}, 1000)});
|
||||
this.annotator.subscribe('flaggedAnnotation', this.updateViewer);
|
||||
this.annotator.subscribe('annotationCreated', function(){setTimeout(function(){self.colorize()}, 1000)});
|
||||
this.annotator.subscribe('externalCallToHighlightTags', function(){setTimeout(function(){self.externalCall()}, 1000)});
|
||||
this.annotator.subscribe('colorEditorTags', this.colorizeEditorTags);
|
||||
};
|
||||
|
||||
HighlightTags.prototype.getHighlightTags = function(){
|
||||
@@ -1023,75 +1028,130 @@ Annotator.Plugin.HighlightTags = (function(_super) {
|
||||
return getColorValues(item)
|
||||
}
|
||||
|
||||
HighlightTags.prototype.colorize = function(){
|
||||
HighlightTags.prototype.colorize = function() {
|
||||
|
||||
var annotations = Array.prototype.slice.call($(".annotator-hl"));
|
||||
for (annNum = 0; annNum < annotations.length; ++annNum){
|
||||
for (annNum = 0; annNum < annotations.length; ++annNum) {
|
||||
var anns = $.data(annotations[annNum],"annotation");
|
||||
if (typeof anns.tags != "undefined" && anns.tags.length == 0) {
|
||||
$(annotations[annNum]).css("background-color","");
|
||||
}
|
||||
if (typeof anns.tags != "undefined" && this.colors !== {}) {
|
||||
if (typeof anns.tags !== "undefined" && anns.tags.length == 0) {
|
||||
|
||||
for(var index = 0; index < anns.tags.length; ++index){
|
||||
if(anns.tags[index].indexOf("flagged-") == -1){
|
||||
if (typeof this.colors[anns.tags[index]] != "undefined") {
|
||||
// image annotations should not change the background of the highlight
|
||||
// only the border so as not to block the image behind it.
|
||||
if (anns.media !== "image") {
|
||||
$(annotations[annNum]).css("background-color", "");
|
||||
} else {
|
||||
$(annotations[annNum]).css("border", "2px solid rgb(255, 255, 255)");
|
||||
$(annotations[annNum]).css("outline", "2px solid rgb(0, 0, 0)");
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof anns.tags !== "undefined" && this.colors !== {}) {
|
||||
|
||||
for (var index = 0; index < anns.tags.length; ++index) {
|
||||
if (anns.tags[index].indexOf("flagged-") == -1) {
|
||||
if (typeof this.colors[anns.tags[index]] !== "undefined") {
|
||||
var finalcolor = this.colors[anns.tags[index]];
|
||||
$(annotations[annNum]).css(
|
||||
"background",
|
||||
// last value, 0.3 is the standard highlight opacity for annotator
|
||||
"rgba(" + finalcolor.red + ", " + finalcolor.green + ", " + finalcolor.blue + ", 0.3)"
|
||||
);
|
||||
}else{
|
||||
$(annotations[annNum]).css(
|
||||
"background",
|
||||
// returns the value to the inherited value without the above
|
||||
""
|
||||
);
|
||||
// if it's a text change the background
|
||||
if (anns.media !== "image") {
|
||||
$(annotations[annNum]).css(
|
||||
"background",
|
||||
// last value, 0.3 is the standard highlight opacity for annotator
|
||||
"rgba(" + finalcolor.red + ", " + finalcolor.green + ", " + finalcolor.blue + ", 0.3)"
|
||||
);
|
||||
}
|
||||
// if it's an image change the dark border/outline leave the white one as is
|
||||
else {
|
||||
$(annotations[annNum]).css(
|
||||
"outline",
|
||||
"2px solid rgb(" + finalcolor.red + ", " + finalcolor.green + ", " + finalcolor.blue + ")"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// if the last tag was not predetermined by instrutor background should go back to default
|
||||
if (anns.media !== "image") {
|
||||
$(annotations[annNum]).css(
|
||||
"background",
|
||||
// returns the value to the inherited value without the above
|
||||
""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
$(annotations[annNum]).css("background","");
|
||||
} else {
|
||||
// if there are no tags or predefined colors, keep the background at default
|
||||
if (anns.media !== "image") {
|
||||
$(annotations[annNum]).css("background","");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.annotator.publish('colorizeCompleted');
|
||||
}
|
||||
|
||||
HighlightTags.prototype.updateField = function(field, annotation){
|
||||
|
||||
|
||||
if(this.isFirstTime){
|
||||
var tags = this.options.tag.split(",");
|
||||
var tokensavailable = [];
|
||||
tags.forEach (function(tagnames){
|
||||
lonename = tagnames.split(":");
|
||||
|
||||
tokensavailable.push({'id': lonename[0], 'name':lonename[0]});
|
||||
});
|
||||
$("#tag-input").tokenInput(tokensavailable);
|
||||
this.isFirstTime = false;
|
||||
}
|
||||
$('#token-input-tag-input').attr('placeholder','Add tags...');
|
||||
$('#tag-input').tokenInput('clear');
|
||||
if (typeof annotation.tags != "undefined") {
|
||||
for (tagnum = 0; tagnum < annotation.tags.length; tagnum++){
|
||||
var n = annotation.tags[tagnum];
|
||||
if (typeof this.annotator.plugins["HighlightTags"] != 'undefined') {
|
||||
if (annotation.tags[tagnum].indexOf("flagged-")==-1){
|
||||
$('#tag-input').tokenInput('add',{'id':n,'name':n});
|
||||
}
|
||||
} else{
|
||||
$('#tag-input').tokenInput('add',{'id':n,'name':n});
|
||||
}
|
||||
HighlightTags.prototype.updateField = function(field, annotation) {
|
||||
// the first time that this plug in runs, the predetermined instructor tags are
|
||||
// added and stored for the dropdown list
|
||||
if(this.isFirstTime) {
|
||||
var tags = this.options.tag.split(",");
|
||||
var tokensavailable = [];
|
||||
|
||||
// tags are given the structure that the dropdown/token function requires
|
||||
tags.forEach (function(tagnames) {
|
||||
lonename = tagnames.split(":");
|
||||
tokensavailable.push({'id': lonename[0], 'name': lonename[0]});
|
||||
});
|
||||
|
||||
// they are then added to the appropriate input for tags in annotator
|
||||
$("#tag-input").tokenInput(tokensavailable);
|
||||
this.isFirstTime = false;
|
||||
}
|
||||
|
||||
$('#token-input-tag-input').attr('placeholder', 'Add tags...');
|
||||
$('#tag-input').tokenInput('clear');
|
||||
|
||||
// loops through the tags already in the annotation and "add" them to this annotation
|
||||
if (typeof annotation.tags !== "undefined") {
|
||||
for (tagnum = 0; tagnum < annotation.tags.length; tagnum++) {
|
||||
var n = annotation.tags[tagnum];
|
||||
if (typeof this.annotator.plugins["HighlightTags"] !== 'undefined') {
|
||||
// if there are flags, we must ignore them
|
||||
if (annotation.tags[tagnum].indexOf("flagged-") == -1) {
|
||||
$('#tag-input').tokenInput('add',{'id':n,'name':n});
|
||||
}
|
||||
} else {
|
||||
$('#tag-input').tokenInput('add', {'id': n, 'name': n});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.colorizeEditorTags();
|
||||
}
|
||||
|
||||
// this function adds the appropriate color to the tag divs for each annotation
|
||||
HighlightTags.prototype.colorizeEditorTags = function() {
|
||||
var self = this;
|
||||
$.each($('.annotator-editor .token-input-token'), function(key, tagdiv) {
|
||||
// default colors are black for text and the original powder blue (already default)
|
||||
var rgbColor = "";
|
||||
var textColor = "color:#000;";
|
||||
var par = $(tagdiv).find("p");
|
||||
|
||||
// if the tag has a predetermined color attached to it,
|
||||
// then it changes the background and turns text white
|
||||
if (typeof self.colors[par.html()] !== "undefined") {
|
||||
var finalcolor = self.colors[par.html()];
|
||||
rgbColor = "background-color:rgba(" + finalcolor.red + ", " + finalcolor.green + ", " + finalcolor.blue + ", 0.5);";
|
||||
textColor = "color:#fff;";
|
||||
}
|
||||
|
||||
// note that to change the text color you must change it in the paragraph tag, not the div
|
||||
$(tagdiv).attr('style', rgbColor);
|
||||
par.attr('style', textColor);
|
||||
});
|
||||
}
|
||||
|
||||
//The following function is run when a person hits submit.
|
||||
// The following function is run when a person hits submit.
|
||||
HighlightTags.prototype.pluginSubmit = function(field, annotation) {
|
||||
var tokens = Array.prototype.slice.call($(".token-input-input-token").parent().find('.token-input-token'));
|
||||
var arr = [];
|
||||
@@ -1102,41 +1162,63 @@ Annotator.Plugin.HighlightTags = (function(_super) {
|
||||
annotation.tags = arr;
|
||||
}
|
||||
|
||||
//The following allows you to edit the annotation popup when the viewer has already
|
||||
//hit submit and is just viewing the annotation.
|
||||
// The following allows you to edit the annotation popup when the viewer has already
|
||||
// hit submit and is just viewing the annotation.
|
||||
HighlightTags.prototype.updateViewer = function(field, annotation) {
|
||||
if (typeof annotation.tags != "undefined") {
|
||||
|
||||
// if there are no tags, the space for tags in the pop up is removed and function ends
|
||||
if (annotation.tags.length == 0) {
|
||||
$(field).remove();
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise we prepare to loop through them
|
||||
var nonFlagTags = true;
|
||||
var tokenList = "<ul class=\"token-input-list\">";
|
||||
|
||||
for (tagnum = 0; tagnum < annotation.tags.length; ++tagnum){
|
||||
if (typeof this.annotator.plugins["Flagging"] != 'undefined') {
|
||||
if (annotation.tags[tagnum].indexOf("flagged-")==-1){
|
||||
tokenList += "<li class=\"token-input-token\"><p>"+ annotation.tags[tagnum]+"</p></span></li>";
|
||||
if (typeof this.annotator.plugins["Flagging"] !== 'undefined') {
|
||||
// once again we ingore flags
|
||||
if (annotation.tags[tagnum].indexOf("flagged-") == -1) {
|
||||
|
||||
// once again, defaults are black for text and powder blue default from token function
|
||||
var rgbColor = "";
|
||||
var textColor = "#000";
|
||||
|
||||
// if there is a color associated with the tag, it will change the background
|
||||
// and change the text to white
|
||||
if (typeof this.colors[annotation.tags[tagnum]] !== "undefined") {
|
||||
var finalcolor = this.colors[annotation.tags[tagnum]];
|
||||
rgbColor = "style=\"background-color:rgba(" + finalcolor.red + ", " + finalcolor.green + ", " + finalcolor.blue + ", 0.5);\"";
|
||||
textColor = "#fff";
|
||||
}
|
||||
|
||||
// note: to change text color you need to do it in the paragrph tag not the div
|
||||
tokenList += "<li class=\"token-input-token\"" + rgbColor + "><p style=\"color: " + textColor + ";\">"+ annotation.tags[tagnum]+"</p></span></li>";
|
||||
nonFlagTags = false;
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
tokenList += "<li class=\"token-input-token\"><p>"+ annotation.tags[tagnum]+"</p></span></li>";
|
||||
}
|
||||
}
|
||||
tokenList += "</ul>";
|
||||
$(field).append(tokenList);
|
||||
|
||||
// the field for tags is removed also if all the tags ended up being flags
|
||||
if (nonFlagTags) {
|
||||
$(field).remove();
|
||||
}
|
||||
|
||||
} else{
|
||||
} else {
|
||||
$(field).remove();
|
||||
}
|
||||
this.annotator.publish("finishedDrawingTags");
|
||||
}
|
||||
|
||||
//The following will call the colorize function during an external call and then return
|
||||
//an event signaling completion.
|
||||
HighlightTags.prototype.externalCall = function(){
|
||||
// The following will call the colorize function during an external call and then return
|
||||
// an event signaling completion.
|
||||
HighlightTags.prototype.externalCall = function() {
|
||||
this.colorize();
|
||||
this.annotator.publish('finishedExternalCallToHighlightTags');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user