From 95c073836167ee9d6e2804148ec89a8a9abe89f8 Mon Sep 17 00:00:00 2001 From: lduarte1991 Date: Thu, 19 Jun 2014 13:52:44 -0400 Subject: [PATCH 1/2] Annotation Tools: Removed background-color from image annotations - Also fixed edit bug when annotation is not created first - Also fixed indentation issues --- .../js/vendor/ova/OpenSeaDragonAnnotation.js | 77 ++++++++++--------- common/static/js/vendor/ova/catch/js/catch.js | 34 ++++---- 2 files changed, 58 insertions(+), 53 deletions(-) diff --git a/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js b/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js index 938af6e8d3..e4ef681ec4 100644 --- a/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js +++ b/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js @@ -71,8 +71,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. this.isDrawing = false; //if the user is drawing something this.rectPosition = undefined; - //Init - this.init(); + //Init + this.init(); }; //-- Methods @@ -167,7 +167,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. var rectPosition = an.rangePosition; //Span span.className = "annotator-hl"; - span.style.border = '1px solid rgba(0,0,0,0.5)'; + span.style.border = '2px solid rgba(0,0,0,0.5)'; + span.style.background = 'rgba(0,0,0,0)'; var onAnnotationMouseMove = this.__bind(this._onAnnotationMouseMove,this); var onAnnotationClick = this.__bind(this._onAnnotationClick,this); $.addEvent(span, "mousemove", onAnnotationMouseMove, true); @@ -219,8 +220,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. var position = mouse.minus( elementPosition ); viewer.innerTracker.setTracking(false); this.rect = document.createElement('div'); - this.rect.style.background = 'rgba(0,0,0,0.25)'; - this.rect.style.border = '1px solid rgba(0,0,0,0.5)'; + this.rect.style.background = 'rgba(0,0,0,0)'; + this.rect.style.border = '2px solid rgba(0,0,0,0.5)'; this.rect.style.position = 'absolute'; this.rect.className = 'DrawingRect'; //set the initial position @@ -306,7 +307,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. var maxx = l + w; var maxy = t + h; this.style.background = (y <= maxy && y >= t) && (x <= maxx && x >= l)? - 'rgba(12, 150, 0, 0.3)':'rgba(255, 255, 10, 0.3)'; + 'rgba(255, 255, 10, 0.05)':'rgba(0, 0, 0, 0)'; return (y <= maxy && y >= t) && (x <= maxx && x >= l)? jQuery(this).data("annotation") : null; }); //show the annotation in the viewer @@ -537,13 +538,13 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { _ref = OpenSeaDragon.__super__.constructor.apply(this, arguments); this.__indexOf = [].indexOf; if(!this.__indexOf){ - this.__indexOf = function(item) { - for (var i = 0, l = this.length; i < l; i++) { - if (i in this && this[i] === item) - return i; - } - return -1; - } + this.__indexOf = function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (i in this && this[i] === item) + return i; + } + return -1; + } } return _ref; @@ -586,29 +587,31 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { if (this.EditOpenSeaDragonAn()){ var annotator = this.annotator; var osda = annotator.osda; - var position = osda.rectPosition; + var position = osda.rectPosition || {}; var isNew = typeof annotation.media=='undefined'; - if (typeof annotation.media == 'undefined') annotation.media = "image"; // - media - annotation.target = annotation.target || {}; // - target - annotation.target.container = osda.viewer.id || ""; // - target.container - //Save source url - var source = osda.viewer.source; - var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; - var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; - annotation.target.src = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); // - target.src (media source) - annotation.target.ext = source.fileFormat || ""; // - target.ext (extension) - annotation.bounds = osda.viewer.drawer.viewport.getBounds() || {}; // - bounds + if(isNew){ + if (typeof annotation.media == 'undefined') annotation.media = "image"; // - media + annotation.target = annotation.target || {}; // - target + annotation.target.container = osda.viewer.id || ""; // - target.container + //Save source url + var source = osda.viewer.source; + var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; + var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; + annotation.target.src = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); // - target.src (media source) + annotation.target.ext = source.fileFormat || ""; // - target.ext (extension) + annotation.bounds = osda.viewer.drawer.viewport.getBounds() || {}; // - bounds - var finalimagelink = source["@id"].replace("/info.json", ""); - var highlightX = Math.round(position.left * source["width"]); - var highlightY = Math.round(position.top * source["width"]); - var highlightWidth = Math.round(position.width * source["width"]); - var highlightHeight = Math.round(position.height * source["width"]); - annotation.target.thumb = finalimagelink + "/" + highlightX + "," + highlightY + "," + highlightWidth + "," + highlightHeight + "/full/0/native." + source["formats"][0]; - if(isNew) annotation.rangePosition = position || {}; // - rangePosition - annotation.updated = new Date().toISOString(); // - updated - if (typeof annotation.created == 'undefined') - annotation.created = annotation.updated; // - created + var finalimagelink = source["@id"].replace("/info.json", ""); + var highlightX = Math.round(position.left * source["width"]); + var highlightY = Math.round(position.top * source["width"]); + var highlightWidth = Math.round(position.width * source["width"]); + var highlightHeight = Math.round(position.height * source["width"]); + annotation.target.thumb = finalimagelink + "/" + highlightX + "," + highlightY + "," + highlightWidth + "," + highlightHeight + "/full/0/native." + source["formats"][0]; + if(isNew) annotation.rangePosition = position || {}; // - rangePosition + annotation.updated = new Date().toISOString(); // - updated + if (typeof annotation.created == 'undefined') + annotation.created = annotation.updated; // - created + } }else{ if (typeof annotation.media == 'undefined') annotation.media = "text"; // - media @@ -691,7 +694,7 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { //-- Viewer function hideViewer(){ jQuery(annotator.osda.viewer.canvas.parentNode).find('.annotator-hl').map(function() { - return this.style.background = 'rgba(255, 255, 10, 0.3)'; + return this.style.background = 'rgba(0, 0, 0, 0)'; }); annotator.viewer.unsubscribe("hide", hideViewer); }; @@ -791,8 +794,8 @@ OpenSeadragonAnnotation = function (element, options) { if (typeof Annotator.Plugin["Flagging"] === 'function') this.annotator.addPlugin("Flagging"); - if (typeof Annotator.Plugin["HighlightTags"] === 'function') - this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags); + /*if (typeof Annotator.Plugin["HighlightTags"] === 'function') + this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags);*/ //- OpenSeaDragon this.viewer = OpenSeadragon(options.optionsOpenSeadragon); diff --git a/common/static/js/vendor/ova/catch/js/catch.js b/common/static/js/vendor/ova/catch/js/catch.js index a45b778d99..16f1ba125e 100644 --- a/common/static/js/vendor/ova/catch/js/catch.js +++ b/common/static/js/vendor/ova/catch/js/catch.js @@ -614,24 +614,24 @@ CatchAnnotation.prototype = { tot = typeof annotations !='undefined'?annotations.length:0, attempts = 0; // max 100 if(annotation.media == "image"){ - self.refreshCatch(true); - self.checkTotAnnotations(); + self.refreshCatch(true); + self.checkTotAnnotations(); } else { //This is to watch the annotations object, to see when is deleted the annotation - var ischanged = function(){ - var new_tot = annotator.plugins['Store'].annotations.length; - if (attempts<100) - setTimeout(function(){ - if (new_tot != tot){ - self.refreshCatch(true); - self.checkTotAnnotations(); - }else{ - attempts++; - ischanged(); - } - },100); //wait for the change in the annotations - }; - ischanged(); + var ischanged = function(){ + var new_tot = annotator.plugins['Store'].annotations.length; + if (attempts<100) + setTimeout(function(){ + if (new_tot != tot){ + self.refreshCatch(true); + self.checkTotAnnotations(); + }else{ + attempts++; + ischanged(); + } + },100); //wait for the change in the annotations + }; + ischanged(); } }); annotator.subscribe("annotationCreated", function (annotation){ @@ -826,6 +826,7 @@ CatchAnnotation.prototype = { } for(var item in allannotations){ var an = allannotations[item]; + an.highlights[0].style.background = "rgba(0,0,0,0)"; if (typeof an.id!='undefined' && an.id == osdaId){//this is the annotation var bounds = new OpenSeadragon.Rect(an.bounds.x, an.bounds.y, an.bounds.width, an.bounds.height); osda.viewer.viewport.fitBounds(bounds, false); @@ -833,6 +834,7 @@ CatchAnnotation.prototype = { console.log(an.target.container); $('html,body').animate({scrollTop: $("#"+an.target.container).offset().top}, 'slow'); + an.highlights[0].style.background = "rgba(255,255,10,0.2)"; } } }, From cd9c6c5f3e9c98f365b83b0933f33a7632a8843d Mon Sep 17 00:00:00 2001 From: lduarte1991 Date: Sat, 21 Jun 2014 16:25:53 -0400 Subject: [PATCH 2/2] Annotations Tools: Commented OSDA code and added clarification to catch changes --- .../js/vendor/ova/OpenSeaDragonAnnotation.js | 405 +++++++++++++----- common/static/js/vendor/ova/catch/js/catch.js | 19 +- 2 files changed, 325 insertions(+), 99 deletions(-) diff --git a/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js b/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js index e4ef681ec4..37f4fdc8f5 100644 --- a/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js +++ b/common/static/js/vendor/ova/OpenSeaDragonAnnotation.js @@ -25,23 +25,37 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. self = this, isOpenViewer = false; + /** + * Sets up a call so that every time the OpenSeaDragon instance is opened + * it triggers the annotations to be redrawn. + */ this.addHandler("open", function() { isOpenViewer = true; if (typeof self.annotationInstance!='undefined') self.annotationInstance.refreshDisplay(); }); - - annotator - //-- Finished the Annotator DOM + /** + * This function is called once annotator has loaded the annotations. + * It will then wait until the OSD instance has loaded to start drawing + * the annotations. + * @param Array annotations list of annotations from annotator instance + */ .subscribe("annotationsLoaded", function (annotations){ if (!self.annotationInstance) { + + //annotation instance should include the OSD item and annotator self.annotationInstance = new $._annotation({ viewer: self, annotator: annotator, }); + + //this collection of items is included as an item of annotator so + //that there is a method to communicate back and forth. annotator.osda = self.annotationInstance; - //Wait until viewer is opened + + //Because it takes a while for both OSD to open and for annotator + //to get items from the backend, we wait until we get the "open" call function refreshDisplay(){ if(!isOpenViewer){ setTimeout(refreshDisplay,200); @@ -55,14 +69,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } }); }; - // INIT annotation + + /** + * Instance of the annotation package including OSD and Annotator + * @constructor + */ $._annotation = function(options) { //options options = options || {}; if (!options.viewer) { throw new Error("A viewer must be specified."); } - + //variables this.viewer = options.viewer; this.annotator = options.annotator; @@ -77,6 +95,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-- Methods $._annotation.prototype = { + /** + * This function makes sure that the OSD buttons are created, that the + * panning and zooming functionality is created and the annotation events. + */ init: function(){ var viewer = this.viewer; @@ -97,78 +119,122 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //Viewer events var self = this; }, + + /** + * This function is called when the user changed from panning/zooming mode to + * annotation creation mode. It allows the annotator to accept the creation of + * a new annotation. + */ newAnnotation:function(){ var annotator = this.annotator; - //This variable is to say the editor that we want create an image annotation + //This variable tells editor that we want create an image annotation annotator.editor.OpenSeaDragon = this.viewer.id; - + + //allows the adder to actually show up annotator.adder.show(); + //takes into account the various wrappers and instances to put the shape + //over the correct place. this._setOverShape(annotator.adder); //Open a new annotator dialog annotator.onAdderClick(); }, + + /** + * This function simply allows the editor to pop up with the given annotation. + * @param {Object} annotation The annotation item from the backend server. + * @param {TinyMCEEditor} editor The item that pops up when you edit an annotation. + */ editAnnotation: function(annotation,editor){ - //This will be usefull when we are going to edit an annotation. + //Stupid check: is the annotation you're trying to edit an image? if (this._isOpenSeaDragon(annotation)){ - //this.hideDisplay(); + var editor = editor || this.annotator.editor; - //set the editor over the range slider + //set the editor over the highlighted element this._setOverShape(editor.element); editor.checkOrientation(); - //This variable is to say the editor that we want create an image annotation + //makes sure that we are making an image annotation editor.OpenSeaDragon = this.viewer.id; } }, + + /** + * This function gets the annotations from the last annotator query and sorts + * them and draws them onto the OpenSeaDragon instance. It also publishes + * a notification in case the colorize the annotations. + */ refreshDisplay: function(){ var allannotations = this.annotator.plugins['Store'].annotations; var annotator = this.annotator; - //Sort by date the Array + //Sort the annotations by date this._sortByDate(allannotations); - //remove all the overlays + //remove all of the overlays this.viewer.drawer.clearOverlays(); for (var item in allannotations) { var an = allannotations[item]; //check if the annotation is an OpenSeaDragon annotation - if (this._isOpenSeaDragon(an)) - this.drawRect(an); - annotator.publish('colorizeHighlight', [an]); - }; + if (this._isOpenSeaDragon(an)){ + this.drawRect(an); + } + } + + //if the colored highlights by tags plugin it is notified to colorize + annotator.publish('colorizeHighlight', [an]); }, + + /** + * This function get notified every time we switch from panning/zooming mode onto + * annotation creation mode. + * @param {Event} e This is the event passed in from the OSD buttons. + */ modeAnnotation:function(e){ this._reset(); var viewer = this.viewer; if (!this.isAnnotating){ + //When annotating, the cursor turns into a crosshair and there is a + //green border around the OSD instance. jQuery('.openseadragon1').css('cursor', 'crosshair'); jQuery('.openseadragon1').css('border', '2px solid rgb(51,204,102)'); e.eventSource.imgGroup.src = this.resolveUrl( viewer.prefixUrl,"newan_hover.png"); e.eventSource.imgRest.src = this.resolveUrl( viewer.prefixUrl,"newan_hover.png"); e.eventSource.imgHover.src = this.resolveUrl( viewer.prefixUrl,"newan_grouphover.png"); }else{ + //Otherwise, the cursor is a cross with four arrows to indicate movement jQuery('.openseadragon1').css('cursor', 'all-scroll'); jQuery('.openseadragon1').css('border', 'inherit'); e.eventSource.imgGroup.src = this.resolveUrl( viewer.prefixUrl,"newan_grouphover.png"); e.eventSource.imgRest.src = this.resolveUrl( viewer.prefixUrl,"newan_rest.png"); e.eventSource.imgHover.src = this.resolveUrl( viewer.prefixUrl,"newan_hover.png"); } + + //toggles the annotating flag this.isAnnotating = !this.isAnnotating?true:false; }, + + /** + * This function takes in an annotation and draws the box indicating the area + * that has been annotated. + * @param {Object} an Annotation item from the list in the Annotator instance. + */ drawRect:function(an){ + //Stupid check: Does this annotation actually have an area of annotation if (typeof an.rangePosition!='undefined'){ + //Sets up the visual aspects of the area for the user var span = document.createElement('span'); var rectPosition = an.rangePosition; - //Span span.className = "annotator-hl"; span.style.border = '2px solid rgba(0,0,0,0.5)'; span.style.background = 'rgba(0,0,0,0)'; + + //Adds listening items for the viewer and editor var onAnnotationMouseMove = this.__bind(this._onAnnotationMouseMove,this); var onAnnotationClick = this.__bind(this._onAnnotationClick,this); $.addEvent(span, "mousemove", onAnnotationMouseMove, true); @@ -176,9 +242,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //Set the object in the div jQuery.data(span, 'annotation', an); + //Add the highlights to the annotation an.highlights = jQuery(span); - + + //Sends the element created to the proper location within the OSD instance var olRect = new OpenSeadragon.Rect(rectPosition.left, rectPosition.top, rectPosition.width, rectPosition.height); return this.viewer.drawer.addOverlay({ element: span, @@ -188,22 +256,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } return false; }, - //Change object(this.rectPosition)the rectangle Position using div element(this.rect) + + /** + * This changes the variable rectPosition to the proper location based on + * screen coordinates and OSD image coordinates. + */ setRectPosition:function(){ + //Get the actual locations of the rectangle var left = parseInt(this.rect.style.left); var top = parseInt(this.rect.style.top); var width = parseInt(this.rect.style.left)+parseInt(this.rect.style.width); var height = parseInt(this.rect.style.top)+parseInt(this.rect.style.height); var startPoint = new $.Point(left,top); var endPoint = new $.Point(width,height); + + //return the proper value of the rectangle this.rectPosition = {left:this._physicalToLogicalXY(startPoint).x, top:this._physicalToLogicalXY(startPoint).y, width:this._physicalToLogicalXY(endPoint).x-this._physicalToLogicalXY(startPoint).x, height:this._physicalToLogicalXY(endPoint).y-this._physicalToLogicalXY(startPoint).y }; }, + /* Handlers */ - _onCanvasMouseDown: function(event,seft) { + + /** + * When the user starts clicking this will create a rectangle that will be a + * temporary position that is to be later scaled via dragging + * @param {Event} event The actual action of clicking down. + */ + _onCanvasMouseDown: function(event) { + + //action is ONLY performed if we are in annotation creation mode if (this.isAnnotating){ var viewer = this.viewer; event.preventDefault(); @@ -239,7 +323,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. viewer.canvas.appendChild(this.rect); } }, + /** + * When the user has clicked and is now dragging to create an annotation area, + * the following function resizes the area selected. + * @param {Event} event The actual action of dragging every time it is dragged. + */ _onCanvasMouseMove: function(event) { + + //of course, this only runs when we are in annotation creation mode and + //when the user has clicked down (and is therefore drawing the rectangle) if (this.isAnnotating && this.isDrawing){ var viewer = this.viewer; @@ -267,7 +359,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. this._setOverShape(this.annotator.adder); } }, + + /** + * This function will finish drawing the rectangle, get its current position + * and then open up the editor to make the annotation. + */ _onDocumentMouseUp: function() { + + //Stupid check: only do it when in annotation creation mode and + //when the user has begun making a rectangle over the annotation area if (this.isAnnotating && this.isDrawing){ var viewer = this.viewer; @@ -286,9 +386,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. this.annotator.editor.checkOrientation(); } }, + + /** + * This function will trigger the viewer to show up whenever an item is hovered + * over and will cause the background color of the area to change a bit. + * @param {Event} event The actual action of moving the mouse over an element. + */ _onAnnotationMouseMove: function(event){ var annotator = this.annotator; var elem = jQuery(event.target).parents('.annotator-hl').andSelf(); + //if there is a opened annotation then show the new annotation mouse over if (typeof annotator!='undefined' && elem.hasClass("annotator-hl") && !this.isDrawing){ //hide the last open viewer @@ -306,8 +413,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. var maxx = l + w; var maxy = t + h; + + //if the current position of the mouse is within the bounds of an area + //change the background of that area to a light yellow to simulate + //a hover. Otherwise, keep it translucent. this.style.background = (y <= maxy && y >= t) && (x <= maxx && x >= l)? 'rgba(255, 255, 10, 0.05)':'rgba(0, 0, 0, 0)'; + return (y <= maxy && y >= t) && (x <= maxx && x >= l)? jQuery(this).data("annotation") : null; }); //show the annotation in the viewer @@ -315,63 +427,76 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. top:$.getMousePosition(event).y, left:$.getMousePosition(event).x, }; + //if the user is hovering over multiple annotation areas, + //they will be stacked as usual if (annotations.length>0) annotator.showViewer(jQuery.makeArray(annotations), mousePosition); } }, + + /** + * This function will zoom/pan the user into the bounding area of the annotation. + * @param {Event} event The actual action of clicking down within an annotation area. + */ _onAnnotationClick: function(event){ + //gets the annotation from the data stored in the element var an = jQuery.data(event.target, 'annotation'); + //gets the bound within the annotation data var bounds = typeof an.bounds!='undefined'?an.bounds:{}; var currentBounds = this.viewer.drawer.viewport.getBounds(); + //if the area is not already panned and zoomed in to the correct area if (typeof bounds.x!='undefined') currentBounds.x = bounds.x; if (typeof bounds.y!='undefined') currentBounds.y = bounds.y; if (typeof bounds.width!='undefined') currentBounds.width = bounds.width; if (typeof bounds.height!='undefined') currentBounds.height = bounds.height; - //change the zoom to the saved + //change the zoom to the saved parameter this.viewer.drawer.viewport.fitBounds(currentBounds); }, - _onAnnotationMouseOut: function(event){ - var annotator = this.annotator; - var elem = jQuery(event.target).parents('.annotator-hl').andSelf(); - //if there is a opened annotation then show the new annotation mouse over - if (typeof annotator!='undefined' && elem.hasClass("annotator-hl") && !this.isDrawing){ - /*jQuery(event.target.parentNode).find('.annotator-hl').map(function() { - return this.style.background = 'rgba(255, 255, 10, 0.3)'; - });*/ - } - }, + /* Utilities */ + /** + * This function will return an array of sorted items + * @param {Array} annotations List of annotations from annotator instance. + * @param {String} type Either 'asc' for ascending or 'desc' for descending + */ _sortByDate: function (annotations,type){ var type = type || 'asc'; //asc => The value [0] will be the most recent date annotations.sort(function(a,b){ + //gets the date from when they were last updated a = new Date(typeof a.updated!='undefined'?createDateFromISO8601(a.updated):''); b = new Date(typeof b.updated!='undefined'?createDateFromISO8601(b.updated):''); + + //orders them based on type passed in if (type == 'asc') return ba?1:0; else return ab?1:0; }); }, + /** + * This function creates the button that will switch back and forth between + * annotation creation mode and panning/zooming mode. + */ _createNewButton:function(){ var viewer = this.viewer; var onFocusHandler = $.delegate( this, onFocus ); var onBlurHandler = $.delegate( this, onBlur ); var onModeAnnotationHandler = $.delegate( this, this.modeAnnotation ); - /* Buttons */ - var viewer = this.viewer; - var self = this; - viewer.modeAnnotation = new $.Button({ - element: viewer.modeAnnotation ? $.getElement( viewer.modeAnnotation ) : null, - clickTimeThreshold: viewer.clickTimeThreshold, - clickDistThreshold: viewer.clickDistThreshold, - tooltip: "New Annotation", - srcRest: self.resolveUrl( viewer.prefixUrl,"newan_rest.png"), - srcGroup: self.resolveUrl( viewer.prefixUrl,"newan_grouphover.png"), - srcHover: self.resolveUrl( viewer.prefixUrl,"newan_hover.png"), - srcDown: self.resolveUrl( viewer.prefixUrl,"newan_pressed.png"), - onRelease: onModeAnnotationHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - }); + /* Buttons */ + var viewer = this.viewer; + var self = this; + viewer.modeAnnotation = new $.Button({ + element: viewer.modeAnnotation ? $.getElement( viewer.modeAnnotation ) : null, + clickTimeThreshold: viewer.clickTimeThreshold, + clickDistThreshold: viewer.clickDistThreshold, + tooltip: "New Annotation", + srcRest: self.resolveUrl( viewer.prefixUrl,"newan_rest.png"), + srcGroup: self.resolveUrl( viewer.prefixUrl,"newan_grouphover.png"), + srcHover: self.resolveUrl( viewer.prefixUrl,"newan_hover.png"), + srcDown: self.resolveUrl( viewer.prefixUrl,"newan_pressed.png"), + onRelease: onModeAnnotationHandler, + onFocus: onFocusHandler, + onBlur: onBlurHandler + }); //- Wrapper Annotation Menu viewer.wrapperAnnotation = new $.ButtonGroup({ @@ -381,6 +506,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. clickTimeThreshold: viewer.clickTimeThreshold, clickDistThreshold: viewer.clickDistThreshold }); + + //area makes sure that the annotation button only appears when everyone is + //allowed to annotate or if you are an instructor if(this.options.viewer.annotation_mode == "everyone" || this.options.viewer.flags){ /* Set elements to the control menu */ viewer.annotatorControl = viewer.wrapperAnnotation.element; @@ -397,14 +525,32 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } } }, + + /** + * This function makes sure that if you're switching to panning/zooming mode, + * the last rectangle you drew (but didn't save) gets destroyed. + */ _reset: function(){ //Find and remove DrawingRect. This is the previous rectangle this._removeElemsByClass('DrawingRect',this.viewer.canvas); + //Show adder and hide editor this.annotator.editor.element[0].style.display = 'none'; }, + + /** + * This function binds the function to the object it was created from + * @param {function} fn This is the function you want to apply + * @param {Object} me This is the object you should pass it to (usually itself) + */ __bind: function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, - // Remove all the elements with a given name inside "inElement" + + /** + * Remove all the elements with a given name inside "inElement" to maintain + * a limited scope. + * @param {String} className Class that should be removed + * @param {HTMLElement} inElement Element in which classes should be removed + */ _removeElemsByClass: function(className,inElement){ var className = className || ''; var inElement = inElement || {}; @@ -413,24 +559,40 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. divs[i].remove(); } }, - //Detect if the annotation is an image annotation + + /** + * Detect if the annotation is an image annotation and there's a target, open + * OSD instance. + * @param {Object} an Annotation from the Annotator instance + */ _isOpenSeaDragon: function (an){ var annotator = this.annotator; var rp = an.rangePosition; + + //Makes sure OSD exists and that annotation is an image annotation + //with a position in the OSD instance var isOpenSeaDragon = (typeof annotator.osda != 'undefined'); var isContainer = (typeof an.target!='undefined' && an.target.container==this.viewer.id ); var isImage = (typeof an.media!='undefined' && an.media=='image'); var isRP = (typeof rp!='undefined'); var isSource = false; - //Save source url - var source = this.viewer.source; - var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; - var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; - var compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); - if(isContainer) isSource = (an.target.src == compareUrl); + + //Double checks that the image being displayed matches the annotations + var source = this.viewer.source; + var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; + var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; + var compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); + if(isContainer) isSource = (an.target.src == compareUrl); + return (isOpenSeaDragon && isContainer && isImage && isRP && isSource); }, + /* Annotator Utilities */ + /** + * Makes sure that absolute x and y values for overlayed section are + * calculated to match area within OSD instance + * @param {HTMLElement} elem Element where shape is overlayed + */ _setOverShape: function(elem){ //Calculate Point absolute positions var rectPosition = this.rectPosition || {}; @@ -443,7 +605,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. var positionCanvas = $.getElementPosition(this.viewer.canvas); var positionAdder = {}; - //Fix with positionCanvas + //Fix with positionCanvas based on annotator wrapper and OSD area startPoint = startPoint.plus(positionCanvas); endPoint = endPoint.plus(positionCanvas); @@ -453,12 +615,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. positionAdder.top = (startPoint.y - positionAnnotator.y) + (endPoint.y - startPoint.y) / 2; //It is not necessary fix with - positionAnnotator.y elem.css(positionAdder); }, + resolveUrl: function( prefix, url ) { return prefix ? prefix + url : url; }, + /* Canvas Utilities */ - // return a point with the values in percentage related to the Image - // point is an object $.Point with the value of the canvas relative coordenates + /** + * Given a point of x and y values in pixels it will return a point with + * percentages in relation to the Image object + * @param {$.Point} point Canvas relative coordinates in pixels + * @return {$.Point} Returns Image relative percentages + */ _physicalToLogicalXY: function(point){ var point = typeof point!='undefined'?point:{}; var boundX = this.viewer.viewport.getBounds(true).x; @@ -473,8 +641,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. y = boundY + ((y / containerSizeY) * boundHeight); return new $.Point(x,y); }, - // return a point with the values in pixels related to the canvas element - // point is an object $.Point with the value of the Image relative percentage + + /** + * Given values in percentage relatives to the image it will return a point in + * pixels related to the canvas element. + * @param {$.Point} point Image relative percentages + * @return {$.Point} Returns canvas relative coordinates in pixels + */ _logicalToPhysicalXY: function(point){ var point = typeof point!='undefined'?point:{}; var boundX = this.viewer.viewport.getBounds(true).x; @@ -492,7 +665,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } /* General functions */ - //initiates an animation to hide the controls + /** + * initiates an animation to hide the controls + */ function beginControlsAutoHide( viewer ) { if ( !viewer.autoHideControls ) { return; @@ -506,7 +681,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. scheduleControlsFade( viewer ); }, viewer.controlsFadeDelay ); } - //stop the fade animation on the controls and show them + /** + * stop the fade animation on the controls and show them + */ function abortControlsAutoHide( viewer ) { var i; viewer.controlsShouldFade = false; @@ -532,12 +709,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Annotator.Plugin.OpenSeaDragon = (function(_super) { __extends(OpenSeaDragon, _super); - //constructor + /** + * Creates an instance of the plugin that interacts with OpenSeaDragon. + * @constructor + */ function OpenSeaDragon() { this.pluginSubmit = __bind(this.pluginSubmit, this); _ref = OpenSeaDragon.__super__.constructor.apply(this, arguments); + + //To facilitate calling items, we want to be able to get the index of a value this.__indexOf = [].indexOf; if(!this.__indexOf){ + + //Basically you iterate through every item on the list, if it matches + //the item you are looking for return the current index, otherwise return -1 this.__indexOf = function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) @@ -552,7 +737,11 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { OpenSeaDragon.prototype.field = null; OpenSeaDragon.prototype.input = null; - + + /** + * This function initiates the editor that will apear when you edit/create an + * annotation and the viewer that appears when you hover over an item. + */ OpenSeaDragon.prototype.pluginInit = function() { //Check that annotator is working if (!Annotator.supported()) { @@ -579,9 +768,11 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { return this.input = $(this.field).find(':input'); } - - - // New JSON for the database + /** + * This function is called by annotator whenever user hits the "Save" Button. It will + * first check to see if the user is editing or creating and then save the + * metadata for the image in an object that will be passed to the backend. + */ OpenSeaDragon.prototype.pluginSubmit = function(field, annotation) { //Select the new JSON for the Object to save if (this.EditOpenSeaDragonAn()){ @@ -590,42 +781,46 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { var position = osda.rectPosition || {}; var isNew = typeof annotation.media=='undefined'; if(isNew){ + //if it's undefined, we know it's an image because the editor within + //the OSD instance was open if (typeof annotation.media == 'undefined') annotation.media = "image"; // - media annotation.target = annotation.target || {}; // - target annotation.target.container = osda.viewer.id || ""; // - target.container + //Save source url var source = osda.viewer.source; var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; annotation.target.src = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); // - target.src (media source) annotation.target.ext = source.fileFormat || ""; // - target.ext (extension) + + //Gets the bounds in order to save them for zooming in and highlight properties annotation.bounds = osda.viewer.drawer.viewport.getBounds() || {}; // - bounds - - var finalimagelink = source["@id"].replace("/info.json", ""); + var finalimagelink = source["@id"].replace("/info.json", ""); var highlightX = Math.round(position.left * source["width"]); var highlightY = Math.round(position.top * source["width"]); var highlightWidth = Math.round(position.width * source["width"]); var highlightHeight = Math.round(position.height * source["width"]); + + //creates a link to the OSD server that contains the image to get + //the thumbnail of the selected portion of the image annotation.target.thumb = finalimagelink + "/" + highlightX + "," + highlightY + "," + highlightWidth + "," + highlightHeight + "/full/0/native." + source["formats"][0]; if(isNew) annotation.rangePosition = position || {}; // - rangePosition + + //updates the dates associated with creation and update annotation.updated = new Date().toISOString(); // - updated if (typeof annotation.created == 'undefined') annotation.created = annotation.updated; // - created } - }else{ - if (typeof annotation.media == 'undefined') - annotation.media = "text"; // - media - annotation.updated = new Date().toISOString(); // - updated - if (typeof annotation.created == 'undefined') - annotation.created = annotation.updated; // - created } return annotation.media; }; - //------ Methods ------// - //Detect if we are creating or editing an OpenSeaDragon annotation + /** + * Detect if we are creating or editing an OpenSeaDragon annotation + */ OpenSeaDragon.prototype.EditOpenSeaDragonAn = function (){ var wrapper = $('.annotator-wrapper').parent()[0], annotator = window.annotator = $.data(wrapper, 'annotator'), @@ -634,31 +829,45 @@ Annotator.Plugin.OpenSeaDragon = (function(_super) { return (isOpenSeaDragon && typeof OpenSeaDragon!='undefined' && OpenSeaDragon!==-1); }; - //Detect if the annotation is an OpenSeaDragon annotation + /** + * Detect if the annotation is an image annotation and there's a target, open + * OSD instance. + * @param {Object} an Annotation from the Annotator instance + */ OpenSeaDragon.prototype.isOpenSeaDragon = function (an){ - var wrapper = $('.annotator-wrapper').parent()[0]; - var annotator = window.annotator = $.data(wrapper, 'annotator'); - var rp = an.rangePosition; - var isOpenSeaDragon = (typeof annotator.osda != 'undefined'); - var isContainer = (typeof an.target!='undefined' && an.target.container==annotator.osda.viewer.id ); - var isImage = (typeof an.media!='undefined' && an.media=='image'); - var isRP = (typeof rp!='undefined'); - var isSource = false; - //Save source url - var source = annotator.osda.viewer.source; - var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; - var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; - var compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); - if(isContainer) isSource = (an.target.src == compareUrl); - return (isOpenSeaDragon && isContainer && isImage && isRP && isSource); + var annotator = this.annotator; + var rp = an.rangePosition; + + //Makes sure OSD exists and that annotation is an image annotation + //with a position in the OSD instance + var isOpenSeaDragon = (typeof annotator.osda != 'undefined'); + var isContainer = (typeof an.target!='undefined' && an.target.container==this.viewer.id ); + var isImage = (typeof an.media!='undefined' && an.media=='image'); + var isRP = (typeof rp!='undefined'); + var isSource = false; + + //Double checks that the image being displayed matches the annotations + var source = this.viewer.source; + var tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:''; + var functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:''; + var compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' '); + if(isContainer) isSource = (an.target.src == compareUrl); + + return (isOpenSeaDragon && isContainer && isImage && isRP && isSource); }; - //Delete OpenSeaDragon Annotation + /** + * Deletes the OSD annotation from Annotator and refreshes display to remove element + * @param {Object} an Annotation object from the Annotator instance + */ OpenSeaDragon.prototype._deleteAnnotation = function(an){ //Remove the annotation of the plugin Store var annotations = this.annotator.plugins['Store'].annotations; + + //Failsafe in case annotation is not immediately removed from annotations list if (annotations.indexOf(an)>-1) annotations.splice(annotations.indexOf(an), 1); + //Refresh the annotations in the display this.annotator.osda.refreshDisplay(); }; @@ -794,8 +1003,8 @@ OpenSeadragonAnnotation = function (element, options) { if (typeof Annotator.Plugin["Flagging"] === 'function') this.annotator.addPlugin("Flagging"); - /*if (typeof Annotator.Plugin["HighlightTags"] === 'function') - this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags);*/ + if (typeof Annotator.Plugin["HighlightTags"] === 'function') + this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags); //- OpenSeaDragon this.viewer = OpenSeadragon(options.optionsOpenSeadragon); diff --git a/common/static/js/vendor/ova/catch/js/catch.js b/common/static/js/vendor/ova/catch/js/catch.js index 16f1ba125e..60b8286a18 100644 --- a/common/static/js/vendor/ova/catch/js/catch.js +++ b/common/static/js/vendor/ova/catch/js/catch.js @@ -636,14 +636,27 @@ CatchAnnotation.prototype = { }); annotator.subscribe("annotationCreated", function (annotation){ var attempts = 0; // max 100 - //wait to get an annotation id + //There is a delay between calls to the backend--especially reading after + //writing. This function listens to when a function is created and waits + //until the server provides it with an annotation id before doing anything + //with it. var ischanged = function(){ if (attempts<100) setTimeout(function(){ if (typeof annotation.id!='undefined'){ + + //once it gets the annotation id, the table refreshes to show + //the edits self.refreshCatch(); if (typeof annotation.parent != 'undefined' && annotation.parent != '0'){ + + //if annotation made was actually a replay to an annotation + //i.e. the only difference is that annotations that are + //not replies have no "parent" var replies = $("[annotationid="+annotation.parent+"]").find(".controlReplies .hideReplies"); + + //forces "Show replies" section to show and then refreshes + //via two clicks replies.show(); replies.click(); replies.click(); @@ -826,6 +839,8 @@ CatchAnnotation.prototype = { } for(var item in allannotations){ var an = allannotations[item]; + //Makes sure that all images are set to transparent in case one was + //previously selected. an.highlights[0].style.background = "rgba(0,0,0,0)"; if (typeof an.id!='undefined' && an.id == osdaId){//this is the annotation var bounds = new OpenSeadragon.Rect(an.bounds.x, an.bounds.y, an.bounds.width, an.bounds.height); @@ -834,6 +849,8 @@ CatchAnnotation.prototype = { console.log(an.target.container); $('html,body').animate({scrollTop: $("#"+an.target.container).offset().top}, 'slow'); + //signifies a selected annotation once OSD has zoomed in on the + //appropriate area, it turns the background a bit yellow an.highlights[0].style.background = "rgba(255,255,10,0.2)"; } }