Commit 32e96681 by lduarte1991

Image Annotation Tool: create xmodule and install js files

parent c2425200
......@@ -139,7 +139,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# response HTML
self.check_components_on_page(
ADVANCED_COMPONENT_TYPES,
['Word cloud', 'Annotation', 'Text Annotation', 'Video Annotation',
['Word cloud', 'Annotation', 'Text Annotation', 'Video Annotation', 'Image Annotation',
'Open Response Assessment', 'Peer Grading Interface', 'openassessment'],
)
......
......@@ -36,6 +36,7 @@ XMODULES = [
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor",
"textannotation = xmodule.textannotation_module:TextAnnotationDescriptor",
"videoannotation = xmodule.videoannotation_module:VideoAnnotationDescriptor",
"imageannotation = xmodule.imageannotation_module:ImageAnnotationDescriptor",
"foldit = xmodule.foldit_module:FolditDescriptor",
"word_cloud = xmodule.word_cloud_module:WordCloudDescriptor",
"hidden = xmodule.hidden_module:HiddenDescriptor",
......
"""
Module for Image annotations using annotator.
"""
from lxml import etree
from pkg_resources import resource_string
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions, html_to_text
from xmodule.annotator_token import retrieve_token
import textwrap
class AnnotatableFields(object):
""" Fields for `ImageModule` and `ImageDescriptor`. """
data = String(help="XML data for the annotation", scope=Scope.content, default=textwrap.dedent("""\
<annotatable>
<instructions>
<p>
Add the instructions to the assignment here.
</p>
</instructions>
<p>
Lorem ipsum dolor sit amet, at amet animal petentium nec. Id augue nemore postulant mea. Ex eam dicant noluisse expetenda, alia admodum abhorreant qui et. An ceteros expetenda mea, tale natum ipsum quo no, ut pro paulo alienum noluisse.
</p>
<json>
navigatorSizeRatio: 0.25,
wrapHorizontal: false,
showNavigator: true,
navigatorPosition: "BOTTOM_LEFT",
showNavigationControl: true,
tileSources: [{
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2009",
Url: "http://static.seadragon.com/content/misc/milwaukee_files/",
TileSize: "254",
Overlap: "1",
Format: "jpg",
ServerFormat: "Default",
Size: {
Width: "15497",
Height: "5378"
}
}
},],
</json>
</annotatable>
"""))
display_name = String(
display_name="Display Name",
help="Display name for this module",
scope=Scope.settings,
default='Image Annotation',
)
instructor_tags = String(
display_name="Tags for Assignments",
help="Add tags that automatically highlight in a certain color using the comma-separated form, i.e. imagery:red,parallelism:blue",
scope=Scope.settings,
default='professor:green,teachingAssistant:blue',
)
annotation_storage_url = String(help="Location of Annotation backend", scope=Scope.settings, default="http://your_annotation_storage.com", display_name="Url for Annotation Storage")
annotation_token_secret = String(help="Secret string for annotation storage", scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name="Secret Token String for Annotation")
class ImageAnnotationModule(AnnotatableFields, XModule):
'''Image Annotation Module'''
js = {
'coffee': [
resource_string(__name__, 'js/src/javascript_loader.coffee'),
resource_string(__name__, 'js/src/html/display.coffee'),
resource_string(__name__, 'js/src/annotatable/display.coffee'),
],
'js': [
resource_string(__name__, 'js/src/collapsible.js'),
]
}
css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]}
icon_class = 'imageannotation'
def __init__(self, *args, **kwargs):
super(ImageAnnotationModule, self).__init__(*args, **kwargs)
xmltree = etree.fromstring(self.data)
self.instructions = self._extract_instructions(xmltree)
self.openseadragonjson = html_to_text(etree.tostring(xmltree.find('json'), encoding='unicode'))
self.user = ""
if self.runtime.get_real_user is not None:
self.user = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
def _extract_instructions(self, xmltree):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
return get_instructions(xmltree)
def get_html(self):
""" Renders parameters to template. """
context = {
'display_name': self.display_name_with_default,
'instructions_html': self.instructions,
'annotation_storage': self.annotation_storage_url,
'token':retrieve_token(self.user, self.annotation_token_secret),
'tag': self.instructor_tags,
'openseadragonjson': self.openseadragonjson,
}
return self.system.render_template('imageannotation.html', context)
class ImageAnnotationDescriptor(AnnotatableFields, RawDescriptor):
''' Image annotation descriptor '''
module_class = ImageAnnotationModule
mako_template = "widgets/raw-edit.html"
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(ImageAnnotationDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([
ImageAnnotationDescriptor.annotation_storage_url,
ImageAnnotationDescriptor.annotation_token_secret,
])
return non_editable_fields
\ No newline at end of file
# -*- coding: utf-8 -*-
"Test for Image Annotation Xmodule functional logic."
import unittest
from mock import Mock
from lxml import etree
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from xmodule.imageannotation_module import ImageAnnotationModule
from . import get_test_system
class ImageAnnotationModuleTestCase(unittest.TestCase):
''' Image Annotation Module Test Case '''
sample_xml = '''
<annotatable>
<instructions><p>Image Test Instructions.</p></instructions>
<json>
navigatorSizeRatio: 0.25,
wrapHorizontal: false,
showNavigator: true,
navigatorPosition: "BOTTOM_LEFT",
showNavigationControl: true,
tileSources: [{
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2009",
Url: "http://static.seadragon.com/content/misc/milwaukee_files/",
TileSize: "254",
Overlap: "1",
Format: "jpg",
ServerFormat: "Default",
Size: {
Width: "15497",
Height: "5378"
}
}
},],
</json>
</annotatable>
'''
def setUp(self):
"""
Makes sure that the Module is declared and mocked with the sample xml above.
"""
self.mod = ImageAnnotationModule(
Mock(),
get_test_system(),
DictFieldData({'data': self.sample_xml}),
ScopeIds(None, None, None, None)
)
def test_extract_instructions(self):
"""
Tests to make sure that the instructions are correctly pulled from the sample xml above.
It also makes sure that if no instructions exist, that it does in fact return nothing.
"""
xmltree = etree.fromstring(self.sample_xml)
expected_xml = u"<div><p>Image Test Instructions.</p></div>"
actual_xml = self.mod._extract_instructions(xmltree) # pylint: disable=W0212
self.assertIsNotNone(actual_xml)
self.assertEqual(expected_xml.strip(), actual_xml.strip())
xmltree = etree.fromstring('<annotatable>foo</annotatable>')
actual = self.mod._extract_instructions(xmltree) # pylint: disable=W0212
self.assertIsNone(actual)
def test_get_html(self):
"""
Tests the function that passes in all the information in the context that will be used in templates/textannotation.html
"""
context = self.mod.get_html()
for key in ['display_name', 'instructions_html', 'annotation_storage', 'token', 'tag', 'openseadragonjson']:
self.assertIn(key, context)
\ No newline at end of file
/*
OpenSeaDragonAnnotation v1.0 (http://)
Copyright (C) 2014 CHS (Harvard University), Daniel Cebrián Robles and Phil Desenne
License: https://github.com/CtrHellenicStudies/OpenSeaDragonAnnotation/blob/master/License.rst
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
(function($) {
$.Viewer.prototype.annotation = function(options) {
//-- wait for plugins --//
var wrapper = jQuery('.annotator-wrapper').parent()[0],
annotator = jQuery.data(wrapper, 'annotator'),
self = this,
isOpenViewer = false;
this.addHandler("open", function() {
isOpenViewer = true;
if (typeof self.annotationInstance!='undefined')
self.annotationInstance.refreshDisplay();
});
annotator
//-- Finished the Annotator DOM
.subscribe("annotationsLoaded", function (annotations){
if (!self.annotationInstance) {
self.annotationInstance = new $._annotation({
viewer: self,
annotator: annotator,
});
annotator.osda = self.annotationInstance;
//Wait until viewer is opened
function refreshDisplay(){
if(!isOpenViewer){
setTimeout(refreshDisplay,200);
}else{
self.annotationInstance.refreshDisplay();
}
}
refreshDisplay();
} else {
self.annotationInstance.refreshDisplay();
}
});
};
// INIT annotation
$._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;
this.options = options;
this.isAnnotating = false; //If the user is annotating
this.isDrawing = false; //if the user is drawing something
//Init
this.init();
};
//-- Methods
$._annotation.prototype = {
init: function(){
var viewer = this.viewer;
//create Buttons
this._createNewButton();
/* canvas Events */
//- Bind canvas functions
var onCanvasMouseDown = this.__bind(this._onCanvasMouseDown,this),
onCanvasMouseMove = this.__bind(this._onCanvasMouseMove,this),
onDocumentMouseUp = this.__bind(this._onDocumentMouseUp,this);
//- Add canvas events
$.addEvent(viewer.canvas, "mousedown", onCanvasMouseDown, true);
$.addEvent(viewer.canvas, "mousemove", onCanvasMouseMove, true);
$.addEvent(document, "mouseup", onDocumentMouseUp, true);
//Viewer events
var self = this;
},
newAnnotation:function(){
var annotator = this.annotator;
//This variable is to say the editor that we want create an image annotation
annotator.editor.OpenSeaDragon = this.viewer.id;
annotator.adder.show();
this._setOverShape(annotator.adder);
//Open a new annotator dialog
annotator.onAdderClick();
},
editAnnotation: function(annotation,editor){
//This will be usefull when we are going to edit an annotation.
if (this._isOpenSeaDragon(annotation)){
//this.hideDisplay();
var editor = editor || this.annotator.editor;
//set the editor over the range slider
this._setOverShape(editor.element);
editor.checkOrientation();
//This variable is to say the editor that we want create an image annotation
editor.OpenSeaDragon = this.viewer.id;
}
},
refreshDisplay: function(){
var allannotations = this.annotator.plugins['Store'].annotations;
var annotator = this.annotator;
//Sort by date the Array
this._sortByDate(allannotations);
//remove all 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]);
};
},
modeAnnotation:function(e){
this._reset();
var viewer = this.viewer;
if (!this.isAnnotating){
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{
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");
}
this.isAnnotating = !this.isAnnotating?true:false;
},
drawRect:function(an){
if (typeof an.rangePosition!='undefined'){
var span = document.createElement('span'),
rectPosition = an.rangePosition;
//Span
span.className = "annotator-hl";
span.style.border = '1px solid rgba(0,0,0,0.5)';
var onAnnotationMouseMove = this.__bind(this._onAnnotationMouseMove,this),
onAnnotationClick = this.__bind(this._onAnnotationClick,this);
$.addEvent(span, "mousemove", onAnnotationMouseMove, true);
$.addEvent(span, "click", onAnnotationClick, true);
//Set the object in the div
jQuery.data(span, 'annotation', an);
//Add the highlights to the annotation
an.highlights = jQuery(span);
var olRect = new OpenSeadragon.Rect(rectPosition.left, rectPosition.top, rectPosition.width, rectPosition.height);
return this.viewer.drawer.addOverlay({
element: span,
location: olRect,
placement: OpenSeadragon.OverlayPlacement.TOP_LEFT
});
}
return false;
},
//Change object(this.rectPosition)the rectangle Position using div element(this.rect)
setRectPosition:function(){
var left = parseInt(this.rect.style.left),
top = parseInt(this.rect.style.top),
width = parseInt(this.rect.style.left)+parseInt(this.rect.style.width),
height = parseInt(this.rect.style.top)+parseInt(this.rect.style.height),
startPoint = new $.Point(left,top),
endPoint = new $.Point(width,height);
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) {
if (this.isAnnotating){
var viewer = this.viewer;
event.preventDefault();
//reset the display
this._reset();
//set mode drawing
this.isDrawing = true;
//Create rect element
var mouse = $.getMousePosition( event ),
elementPosition = $.getElementPosition(viewer.canvas),
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.position = 'absolute';
this.rect.className = 'DrawingRect';
//set the initial position
this.rect.style.top = position.y+"px";
this.rect.style.left = position.x+"px";
this.rect.style.width = "1px";
this.rect.style.height = "1px";
//save the start Position
this.startPosition = position;
//save rectPosition as initial rectangle parameter to Draw in the canvas
this.setRectPosition();
//append Child to the canvas
viewer.canvas.appendChild(this.rect);
}
},
_onCanvasMouseMove: function(event) {
if (this.isAnnotating && this.isDrawing){
var viewer = this.viewer;
//Calculate the new end position
var mouse = $.getMousePosition( event ),
elementPosition = $.getElementPosition(viewer.canvas),
endPosition = mouse.minus( elementPosition );
//retrieve start position
var startPosition = this.startPosition;
var newWidth= endPosition.x-startPosition.x,
newHeight =endPosition.y-startPosition.y;
//Set new position
this.rect.style.width = (newWidth<0) ? (-1*newWidth) +'px' : newWidth +'px';
this.rect.style.left = (newWidth<0) ? (startPosition.x + newWidth) +'px' : startPosition.x +'px';
this.rect.style.height = (newHeight<0) ? (-1*newHeight) +'px' : newHeight +'px';
this.rect.style.top = (newHeight<0) ? (startPosition.y + newHeight) +'px' : startPosition.y +'px';
//Modify the rectPosition with the new this.rect values
this.setRectPosition();
//Show adder and hide editor
this.annotator.editor.element[0].style.display = 'none';
this._setOverShape(this.annotator.adder);
}
},
_onDocumentMouseUp: function() {
if (this.isAnnotating && this.isDrawing){
var viewer = this.viewer;
viewer.innerTracker.setTracking(true);
this.isDrawing = false;
//Set the new position for the rectangle
this.setRectPosition();
//Open Annotator editor
this.newAnnotation();
//Hide adder and show editor
this.annotator.editor.element[0].style.display = 'block';
this._setOverShape(this.annotator.editor.element);
this.annotator.editor.checkOrientation();
}
},
_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
annotator.viewer.hide();
//get the annotation over the mouse
var annotations = jQuery(event.target.parentNode).find('.annotator-hl').map(function() {
var self = jQuery(this),
offset = self.offset(),
l = offset.left,
t = offset.top,
h = self.height(),
w = self.width(),
x = $.getMousePosition(event).x,
y = $.getMousePosition(event).y;
var maxx = l + w,
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)';
return (y <= maxy && y >= t) && (x <= maxx && x >= l)? jQuery(this).data("annotation") : null;
});
//show the annotation in the viewer
var mousePosition = {
top:$.getMousePosition(event).y,
left:$.getMousePosition(event).x,
};
if (annotations.length>0) annotator.showViewer(jQuery.makeArray(annotations), mousePosition);
}
},
_onAnnotationClick: function(event){
var an = jQuery.data(event.target, 'annotation'),
bounds = typeof an.bounds!='undefined'?an.bounds:{},
currentBounds = this.viewer.drawer.viewport.getBounds();
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
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 */
_sortByDate: function (annotations,type){
var type = type || 'asc'; //asc => The value [0] will be the most recent date
annotations.sort(function(a,b){
a = new Date(typeof a.updated!='undefined'?createDateFromISO8601(a.updated):'');
b = new Date(typeof b.updated!='undefined'?createDateFromISO8601(b.updated):'');
if (type == 'asc')
return b<a?-1:b>a?1:0;
else
return a<b?-1:a>b?1:0;
});
},
_createNewButton:function(){
var viewer = this.viewer,
onFocusHandler = $.delegate( this, onFocus ),
onBlurHandler = $.delegate( this, onBlur ),
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
});
//- Wrapper Annotation Menu
viewer.wrapperAnnotation = new $.ButtonGroup({
buttons: [
viewer.modeAnnotation,
],
clickTimeThreshold: viewer.clickTimeThreshold,
clickDistThreshold: viewer.clickDistThreshold
});
/* Set elements to the control menu */
viewer.annotatorControl = viewer.wrapperAnnotation.element;
if( viewer.toolbar ){
viewer.toolbar.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.BOTTOM_RIGHT}
);
}else{
viewer.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.TOP_LEFT}
);
}
},
_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';
},
__bind: function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
// Remove all the elements with a given name inside "inElement"
_removeElemsByClass: function(className,inElement){
var className = className || '',
inElement = inElement || {};
divs = inElement.getElementsByClassName(className);
for(var i = 0; i < divs.length; i++) {
divs[i].remove();
}
},
//Detect if the annotation is an image annotation
_isOpenSeaDragon: function (an){
var annotator = this.annotator,
rp = an.rangePosition,
isOpenSeaDragon = (typeof annotator.osda != 'undefined'),
isContainer = (typeof an.target!='undefined' && an.target.container==this.viewer.id ),
isImage = (typeof an.media!='undefined' && an.media=='image'),
isRP = (typeof rp!='undefined'),
isSource = false;
//Save source url
var source = this.viewer.source,
tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:'';
functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:'',
compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' ');
if(isContainer) isSource = (an.target.src == compareUrl);
return (isOpenSeaDragon && isContainer && isImage && isRP && isSource);
},
/* Annotator Utilities */
_setOverShape: function(elem){
//Calculate Point absolute positions
var rectPosition = this.rectPosition || {},
startPoint = this._logicalToPhysicalXY(new $.Point(rectPosition.left,rectPosition.top)),
endPoint = this._logicalToPhysicalXY(new $.Point(rectPosition.left+rectPosition.width,rectPosition.top+rectPosition.height));
//Calculate Point absolute positions
var wrapper = jQuery('.annotator-wrapper')[0],
positionAnnotator = $.getElementPosition(wrapper),
positionCanvas = $.getElementPosition(this.viewer.canvas),
positionAdder = {};
//Fix with positionCanvas
startPoint = startPoint.plus(positionCanvas);
endPoint = endPoint.plus(positionCanvas);
elem[0].style.display = 'block'; //Show the adder
positionAdder.left = (startPoint.x - positionAnnotator.x) + (endPoint.x - startPoint.x) / 2;
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
_physicalToLogicalXY: function(point){
var point = typeof point!='undefined'?point:{},
boundX = this.viewer.viewport.getBounds(true).x,
boundY = this.viewer.viewport.getBounds(true).y,
boundWidth = this.viewer.viewport.getBounds(true).width,
boundHeight = this.viewer.viewport.getBounds(true).height,
containerSizeX = this.viewer.viewport.getContainerSize().x,
containerSizeY = this.viewer.viewport.getContainerSize().y,
x = typeof point.x!='undefined'?point.x:0,
y = typeof point.y!='undefined'?point.y:0;
x = boundX + ((x / containerSizeX) * boundWidth);
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
_logicalToPhysicalXY: function(point){
var point = typeof point!='undefined'?point:{},
boundX = this.viewer.viewport.getBounds(true).x,
boundY = this.viewer.viewport.getBounds(true).y,
boundWidth = this.viewer.viewport.getBounds(true).width,
boundHeight = this.viewer.viewport.getBounds(true).height,
containerSizeX = this.viewer.viewport.getContainerSize().x,
containerSizeY = this.viewer.viewport.getContainerSize().y,
x = typeof point.x!='undefined'?point.x:0,
y = typeof point.y!='undefined'?point.y:0;
x = (x - boundX) * containerSizeX / boundWidth;
y = (y - boundY) * containerSizeY / boundHeight;
return new $.Point(x,y);
},
}
/* General functions */
//initiates an animation to hide the controls
function beginControlsAutoHide( viewer ) {
if ( !viewer.autoHideControls ) {
return;
}
viewer.controlsShouldFade = true;
viewer.controlsFadeBeginTime =
$.now() +
viewer.controlsFadeDelay;
window.setTimeout( function(){
scheduleControlsFade( viewer );
}, viewer.controlsFadeDelay );
}
//stop the fade animation on the controls and show them
function abortControlsAutoHide( viewer ) {
var i;
viewer.controlsShouldFade = false;
for ( i = viewer.controls.length - 1; i >= 0; i-- ) {
viewer.controls[ i ].setOpacity( 1.0 );
}
}
function onFocus(){
abortControlsAutoHide( this.viewer );
}
function onBlur(){
beginControlsAutoHide( this.viewer );
}
})(OpenSeadragon);
//----------------Plugin for Annotator to setup OpenSeaDragon----------------//
Annotator.Plugin.OpenSeaDragon = (function(_super) {
__extends(OpenSeaDragon, _super);
//constructor
function OpenSeaDragon() {
this.pluginSubmit = __bind(this.pluginSubmit, this);
_ref = OpenSeaDragon.__super__.constructor.apply(this, arguments);
this.__indexOf = [].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;
}
OpenSeaDragon.prototype.field = null;
OpenSeaDragon.prototype.input = null;
OpenSeaDragon.prototype.pluginInit = function() {
//Check that annotator is working
if (!Annotator.supported()) {
return;
}
//-- Editor
this.field = this.annotator.editor.addField({
id: 'osd-input-rangePosition-annotations',
type: 'input', //options (textarea,input,select,checkbox)
submit: this.pluginSubmit,
EditOpenSeaDragonAn: this.EditOpenSeaDragonAn
});
//Modify the element created with annotator to be an invisible span
var select = '<li><span id="osd-input-rangePosition-annotations"></span></li>',
newfield = Annotator.$(select);
Annotator.$(this.field).replaceWith(newfield);
this.field=newfield[0];
//-- Listener for OpenSeaDragon Plugin
this.initListeners();
return this.input = $(this.field).find(':input');
}
// New JSON for the database
OpenSeaDragon.prototype.pluginSubmit = function(field, annotation) {
//Select the new JSON for the Object to save
if (this.EditOpenSeaDragonAn()){
var annotator = this.annotator,
osda = annotator.osda,
position = osda.rectPosition,
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,
tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:'',
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) 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
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
OpenSeaDragon.prototype.EditOpenSeaDragonAn = function (){
var wrapper = $('.annotator-wrapper').parent()[0],
annotator = window.annotator = $.data(wrapper, 'annotator'),
isOpenSeaDragon = (typeof annotator.osda != 'undefined'),
OpenSeaDragon = annotator.editor.OpenSeaDragon;
return (isOpenSeaDragon && typeof OpenSeaDragon!='undefined' && OpenSeaDragon!==-1);
};
//Detect if the annotation is an OpenSeaDragon annotation
OpenSeaDragon.prototype.isOpenSeaDragon = function (an){
var wrapper = $('.annotator-wrapper').parent()[0],
annotator = window.annotator = $.data(wrapper, 'annotator'),
rp = an.rangePosition,
isOpenSeaDragon = (typeof annotator.osda != 'undefined'),
isContainer = (typeof an.target!='undefined' && an.target.container==annotator.osda.viewer.id ),
isImage = (typeof an.media!='undefined' && an.media=='image'),
isRP = (typeof rp!='undefined'),
isSource = false;
//Save source url
var source = annotator.osda.viewer.source,
tilesUrl = typeof source.tilesUrl!='undefined'?source.tilesUrl:'';
functionUrl = typeof source.getTileUrl!='undefined'?source.getTileUrl:'',
compareUrl = tilesUrl!=''?tilesUrl:(''+functionUrl).replace(/\s+/g, ' ');
if(isContainer) isSource = (an.target.src == compareUrl);
return (isOpenSeaDragon && isContainer && isImage && isRP && isSource);
};
//Delete OpenSeaDragon Annotation
OpenSeaDragon.prototype._deleteAnnotation = function(an){
//Remove the annotation of the plugin Store
var annotations = this.annotator.plugins['Store'].annotations;
if (annotations.indexOf(an)>-1)
annotations.splice(annotations.indexOf(an), 1);
//Refresh the annotations in the display
this.annotator.osda.refreshDisplay();
};
//--Listeners
OpenSeaDragon.prototype.initListeners = function (){
var wrapper = $('.annotator-wrapper').parent()[0],
annotator = $.data(wrapper, 'annotator');
var EditOpenSeaDragonAn = this.EditOpenSeaDragonAn,
isOpenSeaDragon = this.isOpenSeaDragon,
self = this;
//local functions
//-- Editor
function annotationEditorHidden(editor) {
if (EditOpenSeaDragonAn()){
annotator.osda._reset();
annotator.osda.refreshDisplay(); //Reload the display of annotations
}
annotator.editor.OpenSeaDragon=-1;
annotator.unsubscribe("annotationEditorHidden", annotationEditorHidden);
};
function annotationEditorShown(editor,annotation) {
annotator.osda.editAnnotation(annotation,editor);
annotator.subscribe("annotationEditorHidden", annotationEditorHidden);
};
//-- Annotations
function annotationDeleted(annotation) {
if (isOpenSeaDragon(annotation))
self._deleteAnnotation(annotation);
};
//-- Viewer
function hideViewer(){
jQuery(annotator.osda.viewer.canvas.parentNode).find('.annotator-hl').map(function() {
return this.style.background = 'rgba(255, 255, 10, 0.3)';
});
annotator.viewer.unsubscribe("hide", hideViewer);
};
function annotationViewerShown(viewer,annotations) {
var wrapper = jQuery('.annotator-wrapper').offset();
//Fix with positionCanvas
var startPoint = {x: parseFloat(viewer.element[0].style.left),
y: parseFloat(viewer.element[0].style.top)};
var separation = viewer.element.hasClass(viewer.classes.invert.y)?5:-5,
newpos = {
top: (startPoint.y - wrapper.top)+separation,
left: (startPoint.x - wrapper.left)
};
viewer.element.css(newpos);
//Remove the time to wait until disapear, to be more faster that annotator by default
viewer.element.find('.annotator-controls').removeClass(viewer.classes.showControls);
annotator.viewer.subscribe("hide", hideViewer);
};
//subscribe to Annotator
annotator.subscribe("annotationEditorShown", annotationEditorShown)
.subscribe("annotationDeleted", annotationDeleted)
.subscribe("annotationViewerShown", annotationViewerShown);
}
return OpenSeaDragon;
})(Annotator.Plugin);
//----------------PUBLIC OBJECT TO CONTROL THE ANNOTATIONS----------------//
//The name of the plugin that the user will write in the html
OpenSeadragonAnnotation = ("OpenSeadragonAnnotation" in window) ? OpenSeadragonAnnotation : {};
OpenSeadragonAnnotation = function (element, options) {
//local variables
var $ = jQuery,
options = options || {};
options.optionsOpenSeadragon = options.optionsOpenSeadragon || {};
options.optionsOSDA = options.optionsOSDA || {};
options.optionsAnnotator = options.optionsAnnotator || {};
//if there isn't store optinos it will create a uri and limit variables for the Back-end of Annotations
if (typeof options.optionsAnnotator.store=='undefined')
options.optionsAnnotator.store = {};
var store = options.optionsAnnotator.store;
if (typeof store.annotationData=='undefined')
store.annotationData = {};
if (typeof store.annotationData.uri=='undefined'){
var uri = location.protocol + '//' + location.host + location.pathname;
store.annotationData.store = {uri:uri};
}
if (typeof store.loadFromSearch=='undefined')
store.loadFromSearch={};
if (typeof store.loadFromSearch.uri=='undefined')
store.loadFromSearch.uri = uri;
if (typeof store.loadFromSearch.limit=='undefined')
store.loadFromSearch.limit = 10000;
//global variables
this.currentUser = null;
//-- Init all the classes --/
//Annotator
this.annotator = $(element).annotator(options.optionsAnnotator.annotator).data('annotator');
//-- Activate all the Annotator plugins --//
if (typeof options.optionsAnnotator.auth!='undefined')
this.annotator.addPlugin('Auth', options.optionsAnnotator.auth);
if (typeof options.optionsAnnotator.permissions!='undefined')
this.annotator.addPlugin("Permissions", options.optionsAnnotator.permissions);
if (typeof options.optionsAnnotator.store!='undefined')
this.annotator.addPlugin("Store", options.optionsAnnotator.store);
if (typeof Annotator.Plugin["Geolocation"] === 'function')
this.annotator.addPlugin("Geolocation",options.optionsAnnotator.geolocation);
if (typeof Annotator.Plugin["Share"] === 'function')
this.annotator.addPlugin("Share",options.optionsAnnotator.share);
if (typeof Annotator.Plugin["RichText"] === 'function')
this.annotator.addPlugin("RichText",options.optionsAnnotator.richText);
if (typeof Annotator.Plugin["Reply"] === 'function')
this.annotator.addPlugin("Reply");
if (typeof Annotator.Plugin["OpenSeaDragon"] === 'function')
this.annotator.addPlugin("OpenSeaDragon");
if (typeof Annotator.Plugin["Flagging"] === 'function')
this.annotator.addPlugin("Flagging");
if (typeof Annotator.Plugin["HighlightTags"] === 'function')
this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags);
//- OpenSeaDragon
this.viewer = OpenSeadragon(options.optionsOpenSeadragon);
//- OpenSeaDragon Plugins
this.viewer.annotation(options.optionsOSDA);
//Set annotator.editor.OpenSeaDragon by default
this.annotator.editor.OpenSeaDragon=-1;
this.options = options;
return this;
}
\ No newline at end of file
/*
Grid Annotation Plugin v1.0
Copyright (C) 2014 Daniel Cebrian Robles and Luis Duarte
License: https://github.com/danielcebrian/share-annotator/blob/master/License.rst
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//The name of the plugin that the user will write in the html
window.CatchAnnotation = ("CatchAnnotation" in window) ? CatchAnnotation : {};
window.CatchSources = ("CatchSources" in window) ? CatchSources : {};
......@@ -76,13 +95,10 @@ annotationMediaSelector:
'<li class="ui-state-default" media="video">'+
'Video'+
'</li>'+
'li class="ui-state-default" media="image">'+
'Image'+
'</li>'+
'</ul>',
// '<div class="selButtonCatch">Text<span class="action">text</span></div>'+
// '<div class="selButtonCatch">Video<span class="action">video</span></div>',
// '<div class="selButtonCatch">Images<span class="action">image</span></div>'+
// '<div class="selButtonCatch">Audio<span class="action">audio</span></div>'+
// '<div class="selButtonCatch">Maps<span class="action">map</span></div>'+
// '<div class="selButtonCatch">3D studio<span class="action">3d</span></div>',
//Main->ContainerRow
annotationItem:
......@@ -162,7 +178,15 @@ annotationRow:
//Main->ContainerRow->DetailRow
annotationDetail:
'<div class="annotationDetail">'+
'{{#if mediatypeforgrid.text}}'+
'<div class="annotationDetail">'+
'{{/if}}'+
'{{#if mediatypeforgrid.video}}'+
'<div class="annotationDetail videoAnnotationDetail">'+
'{{/if}}'+
'{{#if mediatypeforgrid.image}}'+
'<div class="annotationDetail imageAnnotationDetail">'+
'{{/if}}'+
'<div class="detailHeader">'+
'<span class="closeDetailIcon">'+
'<img src="'+root+'closeIcon.png" alt="Hide Details" />'+
......@@ -181,77 +205,25 @@ annotationDetail:
'{{/if}}'+
'</div>'+
'{{#if mediatypeforgrid.text}}'+
'<div class="quote">'+
'<div style="text-align: center">'+
'<div class="quoteItem">“</div><div class="quoteText">{{{ quote }}}</div><div class="quoteItem">”</div></div>'+
'<span class="idAnnotation" style="display:none">{{{ id }}}</span>'+
'<span class="uri" style="display:none">{{{uri}}}</span>'+
'</div>'+
'<div class="body">'+
'{{{ text }}}'+
'</div>'+
'<div class="controlReplies">'+
'<div class="newReply" style="text-decoration:underline">Reply</div>&nbsp;'+
'<div class="hideReplies" style="text-decoration:underline;display:{{#if hasReplies}}block{{else}}none{{/if}}">Show Replies</div>&nbsp;'+
'{{#if authToEditButton}}'+
'<div class="editAnnotation" style="text-decoration:underline">Edit</div>'+
'{{/if}}'+
'{{#if authToDeleteButton}}'+
'<div class="deleteAnnotation" style="text-decoration:underline">Delete</div>'+
'{{/if}}'+
'</div>'+
'<div class="replies"></div>'+
'{{#if tags}}'+
'<div class="tags">'+
'<h3>Tags:</h3>'+
'{{#each tags}}'+
'<div class="tag">'+
'{{this}}'+
'</div>'+
'{{/each}}'+
'</div>'+
'{{/if}}'+
'<div class="controlPanel">'+
//'<img class="privacy_button" src="'+root+'privacy_icon.png" width="36" height="36" alt="Privacy Settings" title="Privacy Settings">'+
// '<img class="groups_button" src="'+root+'groups_icon.png" width="36" height="36" alt="Groups Access" title="Groups Access">'+
//'<img class="share_button" src="'+root+'share_icon.png" width="36" height="36" alt="Share Annotation" title="Share Annotation"/>'+
'</div>'+
'</div>',
//Main->ContainerRow->DetailRow (Video)
videoAnnotationDetail:
'<div class="annotationDetail videoAnnotationDetail">'+
'<div class="detailHeader">'+
'<span class="closeDetailIcon">'+
'<img src="'+root+'closeIcon.png" alt="Hide Details" />'+
'</span>'+
'On {{ updated }} <!--<a href="index.php?r=user/user/view&id={{{user.id}}}">-->{{{ user.name }}}<!--</a>-->{{#if geolocation}}, wrote from {{/if}}'+
'{{#if geolocation}}'+
'<span class="geolocationIcon">'+
'<img src="'+root+'geolocation_icon.png"width="25" height="25" alt="Location Map" title="Show Location Map" data-dropdown="myLocationMap"/>'+
'<span class="idAnnotation" style="display:none">{{{ id }}}</span>'+
'<span class="latitude" style="display:none">{{{ geolocation.latitude }}}</span>'+
'<span class="longitude" style="display:none">{{{ geolocation.longitude }}}</span>'+
'</span>'+
'<div id="myLocationMap" data-dropdown-content class="f-dropdown content">'+
'<div class="map"></div>'+
'</div>'+
'{{/if}}'+
'</div>'+
'{{#if mediatypeforgrid.video}}'+
'<div class="playMediaButton">'+
'Play segment {{{ rangeTime.start }}} - {{{ rangeTime.end }}}'+
'<span class="idAnnotation" style="display:none">{{{ id }}}</span>'+
'<span class="uri" style="display:none">{{{uri}}}</span>'+
'<span class="container" style="display:none">{{{target.container}}}</span>'+
'</div>'+
'{{/if}}'+
'{{#if mediatypeforgrid.image}}'+
'<img src="http://www.paraemigrantes.com/wp-content/themes/daily/images/default-thumb.gif">'+
'{{/if}}'+
'<div class="body">'+
'{{{ text }}}'+
'</div>'+
......@@ -324,8 +296,8 @@ CatchAnnotation = function (element, options) {
$( document ).ready(function() {
self.init();
self.refreshCatch(true);
var moreBut = self.element.find('.annotationListButtons .moreButtonCatch');
moreBut.hide();
var moreBut = self.element.find('.annotationListButtons .moreButtonCatch');
moreBut.hide();
});
return this;
......@@ -343,7 +315,6 @@ CatchAnnotation.prototype = {
"annotationReply",//Main->ContainerRow->Reply
"annotationRow", //Main->ContainerRow->Row
"annotationDetail",//Main->ContainerRow->DetailRow
"videoAnnotationDetail"//Main->ContainerRow->DetailRow (Video)
];
//annotator
var wrapper = $('.annotator-wrapper').parent()[0],
......@@ -400,7 +371,7 @@ CatchAnnotation.prototype = {
evenOrOdd: index % 2 ? "odd" : "even",
openOrClosed: "closed",
annotationRow: self.TEMPLATES.annotationRow(item),
annotationDetail: (mediaType === "video") ? self.TEMPLATES.videoAnnotationDetail(item):self.TEMPLATES.annotationDetail(item),
annotationDetail: self.TEMPLATES.annotationDetail(item),
});
index++;
annotationItems.push(html);
......@@ -442,7 +413,7 @@ CatchAnnotation.prototype = {
//Bind functions
var openAnnotationItem = this.__bind(this._openAnnotationItem,this),
closeAnnotationItem = this.__bind(this._closeAnnotationItem,this),
closeAnnotationItem = this.__bind(this._closeAnnotationItem,this),
onGeolocationClick = this.__bind(this._onGeolocationClick,this),
onPlaySelectionClick = this.__bind(this._onPlaySelectionClick,this),
onShareControlsClick = this.__bind(this._onShareControlsClick,this),
......@@ -493,16 +464,16 @@ CatchAnnotation.prototype = {
changeMedia: function(media) {
var media = media || 'text';
this.options.media = media;
this._refresh();
this._refresh();
this.refreshCatch(true);
this.checkTotAnnotations();
this.checkTotAnnotations();
},
changeUserId: function(userId) {
var userId = userId || '';
this.options.userId = userId;
this._refresh();
this.refreshCatch(true);
this.checkTotAnnotations();
this.checkTotAnnotations();
},
loadAnnotations: function() {
var annotator = this.annotator,
......@@ -520,9 +491,9 @@ CatchAnnotation.prototype = {
//annotator.plugins['Store'].loadAnnotationsFromSearch(loadFromSearch);
//Make sure to be openned all annotations for this pagination
loadFromSearch.limit = this.options.pagination+loadedAn;
loadFromSearch.offset = 0;
annotator.plugins['Store'].loadAnnotationsFromSearch(loadFromSearch);
loadFromSearch.limit = this.options.pagination+loadedAn;
loadFromSearch.offset = 0;
annotator.plugins['Store'].loadAnnotationsFromSearch(loadFromSearch);
//text loading annotations
var moreBut = this.element.find('.annotationListButtons .moreButtonCatch');
......@@ -568,7 +539,6 @@ CatchAnnotation.prototype = {
var moreBut = this.element.find('.annotationListButtons .moreButtonCatch');
moreBut.html('More');
setTimeout();
},
//
......@@ -598,7 +568,7 @@ CatchAnnotation.prototype = {
setTimeout(function(){
if (new_tot != tot){
self.refreshCatch(true);
self.checkTotAnnotations();
self.checkTotAnnotations();
}else{
attempts++;
ischanged();
......@@ -617,7 +587,7 @@ CatchAnnotation.prototype = {
self.refreshCatch();
if (typeof annotation.parent != 'undefined' && annotation.parent != '0'){
var replies = $("[annotationid="+annotation.parent+"]").find(".controlReplies .hideReplies");
replies.show();
replies.show();
replies.click();
replies.click();
}
......@@ -632,7 +602,7 @@ CatchAnnotation.prototype = {
},
__bind: function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
_compileTemplates: function() {
var self = this;
var self = this;
//Change the html tags to functions
this.TEMPLATENAMES.forEach(function(templateName) {
self.TEMPLATES[templateName] = Handlebars.compile(self.HTMLTEMPLATES[templateName]);
......@@ -678,6 +648,9 @@ CatchAnnotation.prototype = {
});//Change to < and > tags
item.plainText = item.plainText.replace(/<\/?[^>]+(>|$)/g, "").replace('&nbsp;',''); //remove all the html tags
item.mediatypeforgrid = {};
item.mediatypeforgrid[item.media] = true;
//Flags
if(!this.options.flags && typeof item.tags != 'undefined' && item.tags.length > 0){
for(var len=item.tags.length, index = len-1; index >= 0; --index){
......@@ -1095,13 +1068,10 @@ CatchAnnotation.prototype = {
annotation = item.data('annotation');
var authorized = permissions.options.userAuthorize('delete', annotation,permissions.user);
if(authorized){
//annotator.deleteAnnotation(annotation);
if(confirm('Would you like to delete this reply?')){
annotator.plugins['Store']._apiRequest('destroy', annotation, function(){});
item.remove();
}
}
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -845,6 +845,8 @@ main_vendor_js = [
'js/vendor/ova/tags-annotator.js',
'js/vendor/ova/flagging-annotator.js',
'js/vendor/ova/jquery-Watch.js',
'js/vendor/ova/openseadragon.js',
'js/vendor/ova/OpenSeaDragonAnnotation.js',
'js/vendor/ova/ova.js',
'js/vendor/ova/catch/js/catch.js',
'js/vendor/ova/catch/js/handlebars-1.1.2.js',
......
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='/static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)}
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/tinymce.full.min.js', raw=True)}" />
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" />
<style type="text/css">
.openseadragon1{
width: 100%;
height: 600px;
cursor: all-scroll;
}
</style>
<div class="annotatable-wrapper">
<div class="annotatable-header">
% if display_name is not UNDEFINED and display_name is not None:
<div class="annotatable-title">${display_name}</div>
% endif
</div>
% if instructions_html is not UNDEFINED and instructions_html is not None:
<div class="annotatable-section shaded">
<div class="annotatable-section-title">
${_('Instructions')}
<a class="annotatable-toggle annotatable-toggle-instructions expanded" href="javascript:void(0)">${_('Collapse Instructions')}</a>
</div>
<div class="annotatable-section-body annotatable-instructions">
${instructions_html}
</div>
</div>
% endif
<div class="annotatable-section">
<div class="annotatable-content">
<div id="imageHolder" class="openseadragon1">
</div>
<div id="catchDIV">
<div class="annotationListContainer">${_('You do not have any notes.')}</div>
</div>
</div>
</div>
</div>
<script>
function onClickHideInstructions(){
//Reset function if there is more than one event handler
$(this).off();
$(this).on('click',onClickHideInstructions);
var hide = $(this).html()=='Collapse Instructions'?true:false,
cls, txt,slideMethod;
txt = (hide ? 'Expand' : 'Collapse') + ' Instructions';
cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']);
slideMethod = (hide ? 'slideUp' : 'slideDown');
$(this).text(txt).removeClass(cls[0]).addClass(cls[1]);
$(this).parents('.annotatable-section:first').find('.annotatable-instructions')[slideMethod]();
}
$('.annotatable-toggle-instructions').on('click', onClickHideInstructions);
//Grab uri of the course
var parts = window.location.href.split("/"),
uri = '',
courseid;
for (var index = 0; index <= 9; index += 1) uri += parts[index]+"/"; //Get the unit url
courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
//Change uri in cms
var lms_location = $('.sidebar .preview-button').attr('href');
if (typeof lms_location!='undefined'){
courseid = parts[4].split(".").join("/");
uri = window.location.protocol;
for (var index = 0; index <= 9; index += 1) uri += lms_location.split("/")[index]+"/"; //Get the unit url
}
var unit_id = $('#sequence-list').find('.active').attr("data-element");
uri += unit_id;
var pagination = 100,
is_staff = !('${user.is_staff}'=='False'),
options = {
optionsAnnotator: {
permissions:{
user: {
id:"${user.email}",
name:"${user.username}"
},
userString: function (user) {
if (user && user.name)
return user.name;
return user;
},
userId: function (user) {
if (user && user.id)
return user.id;
return user;
},
permissions: {
'read': [],
'update': ["${user.email}"],
'delete': ["${user.email}"],
'admin': ["${user.email}"]
},
showViewPermissionsCheckbox: true,
showEditPermissionsCheckbox: false,
userAuthorize: function(action, annotation, user) {
var token, tokens, _i, _len;
if (annotation.permissions) {
tokens = annotation.permissions[action] || [];
if (is_staff){
return true;
}
if (tokens.length === 0) {
return true;
}
for (_i = 0, _len = tokens.length; _i < _len; _i++) {
token = tokens[_i];
if (this.userId(user) === token) {
return true;
}
}
return false;
} else if (annotation.user) {
if (user) {
return this.userId(user) === this.userId(annotation.user);
} else {
return false;
}
}
return true;
},
},
auth: {
token: "${token}"
},
store: {
// The endpoint of the store on your server.
prefix: "${annotation_storage}",
annotationData: {
uri: uri,
},
urls: {
// These are the default URLs.
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
},
loadFromSearch:{
limit:pagination,
offset:0,
uri:uri,
media:'image',
userid:'${user.email}',
}
},
highlightTags:{
tag: "${tag}",
},
richText: {
tinymce:{
selector: "li.annotator-item textarea",
plugins: "image codemirror",
menubar: false,
toolbar_items_size: 'small',
extended_valid_elements : "iframe[src|frameborder|style|scrolling|class|width|height|name|align|id]",
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image rubric | code ",
}
},
},
optionsOpenSeadragon:{
id: "imageHolder",
prefixUrl: "${settings.STATIC_URL}" + "js/vendor/ova/images/",
${openseadragonjson}
},
optionsOSDA:{},
};
tinymce.baseURL = "${settings.STATIC_URL}" + "js/vendor/tinymce/js/tinymce";
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
if (typeof Annotator != 'undefined'){
//remove old instances
if (Annotator._instances.length !== 0) {
$('#imageHolder').annotator("destroy");
}
delete osda;
//Load the plugin Image/Text Annotation
var osda = new OpenSeadragonAnnotation($('#imageHolder'),options);
//Catch
var annotator = osda.annotator,
catchOptions = {
media:'image',
externalLink:false,
imageUrlRoot:imgURLRoot,
showMediaSelector: false,
showPublicPrivate: true,
userId:'${user.email}',
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff
},
Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
}
</script>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment