Commit d2b26991 by Julia Hansbrough

Merge pull request #3969 from lduarte1991/lduarte-harvardx-pr2

Annotation Tool: Urgent update for ChinaX AB Testing
parents 3584d571 dc9ae84e
...@@ -6,7 +6,10 @@ from lxml import etree ...@@ -6,7 +6,10 @@ from lxml import etree
from urlparse import urlparse from urlparse import urlparse
from os.path import splitext, basename from os.path import splitext, basename
from HTMLParser import HTMLParser from HTMLParser import HTMLParser
from xblock.core import Scope, String
# Make '_' a no-op so we can scrape strings
_ = lambda text: text
def get_instructions(xmltree): def get_instructions(xmltree):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """ """ Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
...@@ -53,3 +56,37 @@ def html_to_text(html): ...@@ -53,3 +56,37 @@ def html_to_text(html):
htmlstripper = MLStripper() htmlstripper = MLStripper()
htmlstripper.feed(html) htmlstripper.feed(html)
return htmlstripper.get_data() return htmlstripper.get_data()
class CommonAnnotatorMixin(object):
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")
)
default_tab = String(
display_name=_("Default Annotations Tab"),
help=_("Select which tab will be the default in the annotations table: myNotes, Instructor, or Public."),
scope=Scope.settings,
default="myNotes",
)
# currently only supports one instructor, will build functionality for multiple later
instructor_email = String(
display_name=_("Email for 'Instructor' Annotations"),
help=_("Email of the user that will be attached to all annotations that will be found in 'Instructor' tab."),
scope=Scope.settings,
default="",
)
annotation_mode = String(
display_name=_("Mode for Annotation Tool"),
help=_("Type in number corresponding to following modes: 'instructor' or 'everyone'"),
scope=Scope.settings,
default="everyone",
)
...@@ -7,7 +7,7 @@ from pkg_resources import resource_string ...@@ -7,7 +7,7 @@ from pkg_resources import resource_string
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions, html_to_text from xmodule.annotator_mixin import CommonAnnotatorMixin, get_instructions, html_to_text
from xmodule.annotator_token import retrieve_token from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment from xblock.fragment import Fragment
...@@ -51,21 +51,9 @@ class AnnotatableFields(object): ...@@ -51,21 +51,9 @@ class AnnotatableFields(object):
scope=Scope.settings, scope=Scope.settings,
default='professor:green,teachingAssistant:blue', 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): class ImageAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
'''Image Annotation Module''' '''Image Annotation Module'''
js = { js = {
'coffee': [ 'coffee': [
...@@ -100,12 +88,14 @@ class ImageAnnotationModule(AnnotatableFields, XModule): ...@@ -100,12 +88,14 @@ class ImageAnnotationModule(AnnotatableFields, XModule):
context = { context = {
'display_name': self.display_name_with_default, 'display_name': self.display_name_with_default,
'instructions_html': self.instructions, 'instructions_html': self.instructions,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user, self.annotation_token_secret), 'token': retrieve_token(self.user, self.annotation_token_secret),
'tag': self.instructor_tags, 'tag': self.instructor_tags,
'openseadragonjson': self.openseadragonjson, 'openseadragonjson': self.openseadragonjson,
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
} }
fragment = Fragment(self.system.render_template('imageannotation.html', context)) fragment = Fragment(self.system.render_template('imageannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js") fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js") fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js")
......
...@@ -6,7 +6,7 @@ from pkg_resources import resource_string ...@@ -6,7 +6,7 @@ from pkg_resources import resource_string
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions from xmodule.annotator_mixin import CommonAnnotatorMixin, get_instructions
from xmodule.annotator_token import retrieve_token from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment from xblock.fragment import Fragment
import textwrap import textwrap
...@@ -47,18 +47,6 @@ class AnnotatableFields(object): ...@@ -47,18 +47,6 @@ class AnnotatableFields(object):
scope=Scope.settings, scope=Scope.settings,
default='None', default='None',
) )
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"),
)
diacritics = String( diacritics = String(
display_name=_("Diacritic Marks"), display_name=_("Diacritic Marks"),
help=_("Add diacritic marks to be added to a text using the comma-separated form, i.e. markname;urltomark;baseline,markname2;urltomark2;baseline2"), help=_("Add diacritic marks to be added to a text using the comma-separated form, i.e. markname;urltomark;baseline,markname2;urltomark2;baseline2"),
...@@ -67,7 +55,7 @@ class AnnotatableFields(object): ...@@ -67,7 +55,7 @@ class AnnotatableFields(object):
) )
class TextAnnotationModule(AnnotatableFields, XModule): class TextAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
''' Text Annotation Module ''' ''' Text Annotation Module '''
js = {'coffee': [], js = {'coffee': [],
'js': []} 'js': []}
...@@ -98,9 +86,12 @@ class TextAnnotationModule(AnnotatableFields, XModule): ...@@ -98,9 +86,12 @@ class TextAnnotationModule(AnnotatableFields, XModule):
'source': self.source, 'source': self.source,
'instructions_html': self.instructions, 'instructions_html': self.instructions,
'content_html': self.content, 'content_html': self.content,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user_email, self.annotation_token_secret), 'token': retrieve_token(self.user_email, self.annotation_token_secret),
'diacritic_marks': self.diacritics, 'diacritic_marks': self.diacritics,
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
} }
fragment = Fragment(self.system.render_template('textannotation.html', context)) fragment = Fragment(self.system.render_template('textannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js") fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
......
...@@ -7,7 +7,7 @@ from pkg_resources import resource_string ...@@ -7,7 +7,7 @@ from pkg_resources import resource_string
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions, get_extension from xmodule.annotator_mixin import CommonAnnotatorMixin, get_instructions, get_extension
from xmodule.annotator_token import retrieve_token from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment from xblock.fragment import Fragment
...@@ -45,20 +45,9 @@ class AnnotatableFields(object): ...@@ -45,20 +45,9 @@ class AnnotatableFields(object):
scope=Scope.settings, scope=Scope.settings,
default="" default=""
) )
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 VideoAnnotationModule(AnnotatableFields, XModule):
class VideoAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
'''Video Annotation Module''' '''Video Annotation Module'''
js = { js = {
'coffee': [ 'coffee': [
...@@ -104,8 +93,11 @@ class VideoAnnotationModule(AnnotatableFields, XModule): ...@@ -104,8 +93,11 @@ class VideoAnnotationModule(AnnotatableFields, XModule):
'typeSource': extension, 'typeSource': extension,
'poster': self.poster_url, 'poster': self.poster_url,
'content_html': self.content, 'content_html': self.content,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user_email, self.annotation_token_secret), 'token': retrieve_token(self.user_email, self.annotation_token_secret),
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
} }
fragment = Fragment(self.system.render_template('videoannotation.html', context)) fragment = Fragment(self.system.render_template('videoannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js") fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
......
...@@ -380,19 +380,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ...@@ -380,19 +380,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
clickTimeThreshold: viewer.clickTimeThreshold, clickTimeThreshold: viewer.clickTimeThreshold,
clickDistThreshold: viewer.clickDistThreshold clickDistThreshold: viewer.clickDistThreshold
}); });
if(this.options.viewer.annotation_mode == "everyone" || this.options.viewer.flags){
/* Set elements to the control menu */ /* Set elements to the control menu */
viewer.annotatorControl = viewer.wrapperAnnotation.element; viewer.annotatorControl = viewer.wrapperAnnotation.element;
if( viewer.toolbar ){ if( viewer.toolbar ){
viewer.toolbar.addControl( viewer.toolbar.addControl(
viewer.annotatorControl, viewer.annotatorControl,
{anchor: $.ControlAnchor.BOTTOM_RIGHT} {anchor: $.ControlAnchor.BOTTOM_RIGHT}
); );
}else{ }else{
viewer.addControl( viewer.addControl(
viewer.annotatorControl, viewer.annotatorControl,
{anchor: $.ControlAnchor.TOP_LEFT} {anchor: $.ControlAnchor.TOP_LEFT}
); );
}
} }
}, },
_reset: function(){ _reset: function(){
......
...@@ -379,14 +379,14 @@ ...@@ -379,14 +379,14 @@
display:inline-block; display:inline-block;
color:#302f2f; color:#302f2f;
font-family:arial; font-family:arial;
font-size:15px; font-size:14px;
font-weight:bold; font-weight:bold;
padding:6px 24px; padding:6px 24px;
text-decoration:none; text-decoration:none;
margin: 0px 0px 10px 0px; margin: 0px 0px 10px 0px;
cursor:pointer; cursor:pointer;
width:140px; width:115px;
text-align:center; text-align:center;
} }
...@@ -468,7 +468,7 @@ ...@@ -468,7 +468,7 @@
#mainCatch .searchbox input{ #mainCatch .searchbox input{
margin: 0; margin: 0;
padding: 0; padding: 0;
width: 60%; width: 50%;
margin-left: 10px; margin-left: 10px;
display: inline; display: inline;
float: left; float: left;
...@@ -493,19 +493,28 @@ ...@@ -493,19 +493,28 @@
cursor:pointer; cursor:pointer;
} }
#mainCatch .searchbox .clear-search-icon{
font-size: 12px;
text-decoration: underline;
float: right;
margin-top: 10px;
padding-right: 3px;
cursor:pointer;
}
#mainCatch .searchbox .search-icon:hover{ #mainCatch .searchbox .search-icon:hover{
opacity:0.5; opacity:0.5;
box-shadow: 2px 4px 5px #888888; box-shadow: 2px 4px 5px #888888;
} }
#mainCatch .selectors{ #mainCatch .selectors{
width:40%; width:45%;
position:relative; position:relative;
float:left; float:left;
} }
#mainCatch .searchbox{ #mainCatch .searchbox{
width:60%; width:52%;
position:relative; position:relative;
float:right; float:right;
} }
...@@ -515,6 +524,7 @@ ...@@ -515,6 +524,7 @@
position:relative; position:relative;
padding-right:5px; padding-right:5px;
margin-top:8px; margin-top:8px;
font-size:14px;
} }
#mainCatch .replies .replyItem .deleteReply{ #mainCatch .replies .replyItem .deleteReply{
......
...@@ -42,29 +42,29 @@ annotationList: ...@@ -42,29 +42,29 @@ annotationList:
'</div>'+ '</div>'+
'<div class="annotatedBy field">'+ '<div class="annotatedBy field">'+
'User'+ gettext('User')+
'</div>'+ '</div>'+
'<div class="body field">'+ '<div class="body field">'+
'Annotation'+ gettext('Annotation')+
'</div>'+ '</div>'+
'{{#if videoFormat}}'+ '{{#if videoFormat}}'+
'<div class="start field">'+ '<div class="start field">'+
'Start'+ gettext('Start')+
'</div>'+ '</div>'+
'<div class="end field">'+ '<div class="end field">'+
'End'+ gettext('End')+
'</div>'+ '</div>'+
'{{/if}}'+ '{{/if}}'+
'<div class="totalreplies field">'+ '<div class="totalreplies field">'+
'#Replies'+ gettext('#Replies')+
'</div>'+ '</div>'+
'<div class="annotatedAt field">'+ '<div class="annotatedAt field">'+
'Date posted'+ gettext('Date posted')+
'</div>'+ '</div>'+
'</div>'+ '</div>'+
'</div>'+ '</div>'+
...@@ -73,30 +73,41 @@ annotationList: ...@@ -73,30 +73,41 @@ annotationList:
'{{/each}}'+ '{{/each}}'+
'</div>'+ '</div>'+
'<div class="annotationListButtons">'+ '<div class="annotationListButtons">'+
'<div class="moreButtonCatch">More</div>'+ '<div class="moreButtonCatch">'+gettext('More')+'</div>'+
'</div>', '</div>',
//Main->PublicPrivateInstructor
annotationPublicPrivateInstructor:
'<div class="selectors"><div class="PublicPrivate myNotes active">'+gettext('My Notes')+'<span class="action">myNotes</span></div>'+
'<div class="PublicPrivate instructor"> '+gettext('Instructor')+'<span class="action">instructor</span></div>'+
'<div class="PublicPrivate public"> '+gettext('Public')+'<span class="action">public</span></div></div>'+
'<div class="searchbox"><div class="searchinst">'+gettext('Search')+'</div><select class="dropdown-list">'+
'<option>'+gettext('Users')+'</option>'+
'<option>'+gettext('Tags')+'</option>'+
'<option>'+gettext('Annotation Text')+'</option>'+
'</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div><div class="clear-search-icon" alt="Clear search.">'+gettext('Clear')+'</div></div>',
//Main->PublicPrivate //Main->PublicPrivate
annotationPublicPrivate: annotationPublicPrivate:
'<div class="selectors"><div class="PublicPrivate myNotes active">My Notes<span class="action">myNotes</span></div>'+ '<div class="selectors"><div class="PublicPrivate myNotes active">'+gettext('My Notes')+'<span class="action">myNotes</span></div>'+
'<div class="PublicPrivate public"> Public<span class="action">public</span></div></div>'+ '<div class="PublicPrivate public"> '+gettext('Public')+'<span class="action">public</span></div></div>'+
'<div class="searchbox"><div class="searchinst">Search</div><select class="dropdown-list">'+ '<div class="searchbox"><div class="searchinst">'+gettext('Search')+'</div><select class="dropdown-list">'+
'<option>Users</option>'+ '<option>'+gettext('Users')+'</option>'+
'<option>Tags</option>'+ '<option>'+gettext('Tags')+'</option>'+
'<option>Annotation Text</option>'+ '<option>'+gettext('Annotation Text')+'</option>'+
'</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div></div>', '</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div><div class="clear-search-icon" alt="Clear search.">'+gettext('Clear')+'</div></div>',
//Main->MediaSelector //Main->MediaSelector
annotationMediaSelector: annotationMediaSelector:
'<ul class="ui-tabs-nav">'+ '<ul class="ui-tabs-nav">'+
'<li class="ui-state-default" media="text">'+ '<li class="ui-state-default" media="text">'+
'Text'+ gettext('Text')+
'</li>'+ '</li>'+
'<li class="ui-state-default" media="video">'+ '<li class="ui-state-default" media="video">'+
'Video'+ gettext('Video')+
'</li>'+ '</li>'+
'li class="ui-state-default" media="image">'+ '<li class="ui-state-default" media="image">'+
'Image'+ gettext('Image')+
'</li>'+ '</li>'+
'</ul>', '</ul>',
...@@ -126,7 +137,7 @@ annotationReply: ...@@ -126,7 +137,7 @@ annotationReply:
'<div class="map"></div>'+ '<div class="map"></div>'+
'</div>'+ '</div>'+
'{{/if}}'+ '{{/if}}'+
'<div class="deleteReply">Delete</div>'+ '<div class="deleteReply">'+gettext('Delete')+'</div>'+
'</p>'+ '</p>'+
'<p>'+ '<p>'+
'{{#if this.text}}'+ '{{#if this.text}}'+
...@@ -233,13 +244,13 @@ annotationDetail: ...@@ -233,13 +244,13 @@ annotationDetail:
'</div>'+ '</div>'+
'<div class="controlReplies">'+ '<div class="controlReplies">'+
'<div class="newReply" style="text-decoration:underline">Reply</div>&nbsp;'+ '<div class="newReply" style="text-decoration:underline">'+gettext('Reply')+'</div>&nbsp;'+
'<div class="hideReplies" style="text-decoration:underline;display:{{#if hasReplies}}block{{else}}none{{/if}}">Show Replies</div>&nbsp;'+ '<div class="hideReplies" style="text-decoration:underline;display:{{#if hasReplies}}block{{else}}none{{/if}}">Show Replies</div>&nbsp;'+
'{{#if authToEditButton}}'+ '{{#if authToEditButton}}'+
'<div class="editAnnotation" style="text-decoration:underline">Edit</div>'+ '<div class="editAnnotation" style="text-decoration:underline">'+gettext('Edit')+'</div>'+
'{{/if}}'+ '{{/if}}'+
'{{#if authToDeleteButton}}'+ '{{#if authToDeleteButton}}'+
'<div class="deleteAnnotation" style="text-decoration:underline">Delete</div>'+ '<div class="deleteAnnotation" style="text-decoration:underline">'+gettext('Delete')+'</div>'+
'{{/if}}'+ '{{/if}}'+
'</div>'+ '</div>'+
...@@ -248,7 +259,7 @@ annotationDetail: ...@@ -248,7 +259,7 @@ annotationDetail:
'{{#if tags}}'+ '{{#if tags}}'+
'<div class="tags">'+ '<div class="tags">'+
'<h3>Tags:</h3>'+ '<h3>'+gettext('Tags:')+'</h3>'+
'{{#each tags}}'+ '{{#each tags}}'+
'<div class="tag">'+ '<div class="tag">'+
'{{this}}'+ '{{this}}'+
...@@ -290,14 +301,14 @@ CatchAnnotation = function (element, options) { ...@@ -290,14 +301,14 @@ CatchAnnotation = function (element, options) {
//Reset element an create a new element div //Reset element an create a new element div
element.html('<div id="mainCatch" class="annotationListContainer"></div>'); element.html('<div id="mainCatch" class="annotationListContainer"></div>');
this.current_tab = this.options.default_tab;
//INIT //INIT
var self = this; var self = this;
$( document ).ready(function() { $( document ).ready(function() {
self.init(); self.init();
self.refreshCatch(true); self.refreshCatch(true);
var moreBut = self.element.find('.annotationListButtons .moreButtonCatch'); var moreBut = self.element.find('.annotationListButtons .moreButtonCatch');
moreBut.hide(); moreBut.hide();
}); });
return this; return this;
...@@ -310,6 +321,7 @@ CatchAnnotation.prototype = { ...@@ -310,6 +321,7 @@ CatchAnnotation.prototype = {
this.TEMPLATENAMES = [ this.TEMPLATENAMES = [
"annotationList", //Main "annotationList", //Main
"annotationPublicPrivate", //Main->PublicPrivate "annotationPublicPrivate", //Main->PublicPrivate
"annotationPublicPrivateInstructor", //Main->PublicPrivateInstructor
"annotationMediaSelector", //Main->MediaSelector "annotationMediaSelector", //Main->MediaSelector
"annotationItem", //Main->ContainerRow "annotationItem", //Main->ContainerRow
"annotationReply",//Main->ContainerRow->Reply "annotationReply",//Main->ContainerRow->Reply
...@@ -317,8 +329,8 @@ CatchAnnotation.prototype = { ...@@ -317,8 +329,8 @@ CatchAnnotation.prototype = {
"annotationDetail",//Main->ContainerRow->DetailRow "annotationDetail",//Main->ContainerRow->DetailRow
]; ];
//annotator //annotator
var wrapper = $('.annotator-wrapper').parent()[0], var wrapper = $('.annotator-wrapper').parent()[0];
annotator = $.data(wrapper, 'annotator'); var annotator = $.data(wrapper, 'annotator');
this.annotator = annotator; this.annotator = annotator;
//Subscribe to annotator //Subscribe to annotator
...@@ -336,10 +348,22 @@ CatchAnnotation.prototype = { ...@@ -336,10 +348,22 @@ CatchAnnotation.prototype = {
this.HTMLTEMPLATES = CatchSources.HTMLTEMPLATES(this.options.imageUrlRoot); this.HTMLTEMPLATES = CatchSources.HTMLTEMPLATES(this.options.imageUrlRoot);
this.TEMPLATES = {}; this.TEMPLATES = {};
this._compileTemplates(); this._compileTemplates();
// the default annotations are the user's personal ones instead of instructor
// if the default tab is instructor, we must refresh the catch to pull the ones
// under the instructor's email. passing empty strings as arguments will default
// to pulling the annotations for the email within this.options.userId.
if(this.options.default_tab.toLowerCase() == 'instructor'){
this.options.userId = this.options.instructor_email;
this._refresh('','');
}
}, },
// //
// GLOBAL UTILITIES // GLOBAL UTILITIES
// //
getTemplate: function(templateName){
return this.TEMPLATES[templateName]() || '';
},
refreshCatch: function(newInstance) { refreshCatch: function(newInstance) {
var mediaType = this.options.media || 'text', var mediaType = this.options.media || 'text',
annotationItems = [], annotationItems = [],
...@@ -380,10 +404,16 @@ CatchAnnotation.prototype = { ...@@ -380,10 +404,16 @@ CatchAnnotation.prototype = {
if (newInstance){ if (newInstance){
var videoFormat = (mediaType === "video") ? true:false; var videoFormat = (mediaType === "video") ? true:false;
var publicPrivateTemplate = '';
if (self.options.showPublicPrivate) {
var templateName = this.options.instructor_email ?
"annotationPublicPrivateInstructor" :
"annotationPublicPrivate";
}
el.html(self.TEMPLATES.annotationList({ el.html(self.TEMPLATES.annotationList({
annotationItems: annotationItems, annotationItems: annotationItems,
videoFormat: videoFormat, videoFormat: videoFormat,
PublicPrivate: self.options.showPublicPrivate?self.TEMPLATES.annotationPublicPrivate():'', PublicPrivate: this.getTemplate(templateName),
MediaSelector: self.options.showMediaSelector?self.TEMPLATES.annotationMediaSelector():'', MediaSelector: self.options.showMediaSelector?self.TEMPLATES.annotationMediaSelector():'',
})); }));
}else{ }else{
...@@ -402,10 +432,21 @@ CatchAnnotation.prototype = { ...@@ -402,10 +432,21 @@ CatchAnnotation.prototype = {
//Set PublicPrivate //Set PublicPrivate
var PublicPrivateButtons = el.find('.annotationListButtons .PublicPrivate').removeClass('active'); //reset var PublicPrivateButtons = el.find('.annotationListButtons .PublicPrivate').removeClass('active'); //reset
for (var index=0;index<PublicPrivateButtons.length;index++) { for (var index=0;index<PublicPrivateButtons.length;index++) {
var span = $(PublicPrivateButtons[index]).find('span'), var span = $(PublicPrivateButtons[index]).find('span');
isUser = (typeof self.options.userId!='undefined' && self.options.userId!='' && self.options.userId!=null); if (span.html().toLowerCase()==self.current_tab.toLowerCase()) {
if (isUser && span.html()=="myNotes") $(PublicPrivateButtons[index]).addClass('active'); switch (self.current_tab.toLowerCase()){
else if (!isUser && span.html()=="public") $(PublicPrivateButtons[index]).addClass('active'); case 'public':
self.options.userId = '';
break;
case 'instructor':
self.options.userId = this.options.instructor_email;
break;
default:
self.options.userId = this.annotator.plugins.Permissions.user.id;
break;
}
$(PublicPrivateButtons[index]).addClass('active');
}
} }
//reset all old events //reset all old events
...@@ -423,6 +464,7 @@ CatchAnnotation.prototype = { ...@@ -423,6 +464,7 @@ CatchAnnotation.prototype = {
onControlRepliesClick = this.__bind(this._onControlRepliesClick, this), onControlRepliesClick = this.__bind(this._onControlRepliesClick, this),
onMoreButtonClick = this.__bind(this._onMoreButtonClick, this), onMoreButtonClick = this.__bind(this._onMoreButtonClick, this),
onSearchButtonClick = this.__bind(this._onSearchButtonClick, this), onSearchButtonClick = this.__bind(this._onSearchButtonClick, this),
onClearSearchButtonClick = this.__bind(this._onClearSearchButtonClick, this),
onDeleteReplyButtonClick = this.__bind(this._onDeleteReplyButtonClick, this), onDeleteReplyButtonClick = this.__bind(this._onDeleteReplyButtonClick, this),
onZoomToImageBoundsButtonClick = this.__bind(this._onZoomToImageBoundsButtonClick, this); onZoomToImageBoundsButtonClick = this.__bind(this._onZoomToImageBoundsButtonClick, this);
...@@ -463,6 +505,9 @@ CatchAnnotation.prototype = { ...@@ -463,6 +505,9 @@ CatchAnnotation.prototype = {
//Search Button //Search Button
el.on("click",".searchbox .search-icon", onSearchButtonClick); el.on("click",".searchbox .search-icon", onSearchButtonClick);
//Clear Search Button
el.on("click",".searchbox .clear-search-icon", onClearSearchButtonClick);
//Delete Reply Button //Delete Reply Button
el.on("click", ".replies .replyItem .deleteReply", onDeleteReplyButtonClick); el.on("click", ".replies .replyItem .deleteReply", onDeleteReplyButtonClick);
...@@ -471,16 +516,16 @@ CatchAnnotation.prototype = { ...@@ -471,16 +516,16 @@ CatchAnnotation.prototype = {
changeMedia: function(media) { changeMedia: function(media) {
var media = media || 'text'; var media = media || 'text';
this.options.media = media; this.options.media = media;
this._refresh(); this._refresh();
this.refreshCatch(true); this.refreshCatch(true);
this.checkTotAnnotations(); this.checkTotAnnotations();
}, },
changeUserId: function(userId) { changeUserId: function(userId) {
var userId = userId || ''; var userId = userId || '';
this.options.userId = userId; this.options.userId = userId;
this._refresh(); this._refresh();
this.refreshCatch(true); this.refreshCatch(true);
this.checkTotAnnotations(); this.checkTotAnnotations();
}, },
loadAnnotations: function() { loadAnnotations: function() {
var annotator = this.annotator, var annotator = this.annotator,
...@@ -568,21 +613,26 @@ CatchAnnotation.prototype = { ...@@ -568,21 +613,26 @@ CatchAnnotation.prototype = {
var annotations = annotator.plugins['Store'].annotations, var annotations = annotator.plugins['Store'].annotations,
tot = typeof annotations !='undefined'?annotations.length:0, tot = typeof annotations !='undefined'?annotations.length:0,
attempts = 0; // max 100 attempts = 0; // max 100
if(annotation.media == "image"){
self.refreshCatch(true);
self.checkTotAnnotations();
} else {
//This is to watch the annotations object, to see when is deleted the annotation //This is to watch the annotations object, to see when is deleted the annotation
var ischanged = function(){ var ischanged = function(){
var new_tot = annotator.plugins['Store'].annotations.length; var new_tot = annotator.plugins['Store'].annotations.length;
if (attempts<100) if (attempts<100)
setTimeout(function(){ setTimeout(function(){
if (new_tot != tot){ if (new_tot != tot){
self.refreshCatch(true); self.refreshCatch(true);
self.checkTotAnnotations(); self.checkTotAnnotations();
}else{ }else{
attempts++; attempts++;
ischanged(); ischanged();
} }
},100); //wait for the change in the annotations },100); //wait for the change in the annotations
}; };
ischanged(); ischanged();
}
}); });
annotator.subscribe("annotationCreated", function (annotation){ annotator.subscribe("annotationCreated", function (annotation){
var attempts = 0; // max 100 var attempts = 0; // max 100
...@@ -770,6 +820,10 @@ CatchAnnotation.prototype = { ...@@ -770,6 +820,10 @@ CatchAnnotation.prototype = {
var allannotations = this.annotator.plugins['Store'].annotations, var allannotations = this.annotator.plugins['Store'].annotations,
osda = this.annotator.osda; osda = this.annotator.osda;
if(this.options.externalLink){
uri += (uri.indexOf('?') >= 0)?'&osdaId='+osdaId:'?osdaId='+osdaId;
location.href = uri;
}
for(var item in allannotations){ for(var item in allannotations){
var an = allannotations[item]; var an = allannotations[item];
if (typeof an.id!='undefined' && an.id == osdaId){//this is the annotation if (typeof an.id!='undefined' && an.id == osdaId){//this is the annotation
...@@ -1011,8 +1065,18 @@ CatchAnnotation.prototype = { ...@@ -1011,8 +1065,18 @@ CatchAnnotation.prototype = {
userId = ''; userId = '';
//Get userI //Get userI
userId = (action.html()=="myNotes")? this.annotator.plugins.Permissions.user.id : ''; switch (action.html()){
case 'public':
userId = '';
break;
case 'instructor':
userId = this.options.instructor_email;
break;
default:
userId = this.annotator.plugins.Permissions.user.id;
break;
}
this.current_tab = action.html();
//Change userid and refresh //Change userid and refresh
this.changeUserId(userId); this.changeUserId(userId);
}, },
...@@ -1069,6 +1133,9 @@ CatchAnnotation.prototype = { ...@@ -1069,6 +1133,9 @@ CatchAnnotation.prototype = {
this._refresh(searchtype,searchInput); this._refresh(searchtype,searchInput);
}, },
_onClearSearchButtonClick: function(evt){
this._refresh('','');
},
_clearAnnotator: function(){ _clearAnnotator: function(){
var annotator = this.annotator, var annotator = this.annotator,
store = annotator.plugins.Store, store = annotator.plugins.Store,
......
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import Http404 from django.http import Http404
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from notes.models import Note from notes.models import Note
from notes.utils import notes_enabled_for_course from notes.utils import notes_enabled_for_course
...@@ -10,12 +11,12 @@ from xmodule.annotator_token import retrieve_token ...@@ -10,12 +11,12 @@ from xmodule.annotator_token import retrieve_token
@login_required @login_required
def notes(request, course_id): def notes(request, course_id):
''' Displays the student's notes. ''' ''' Displays the student's notes. '''
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course = get_course_with_access(request.user, 'load', course_id) course = get_course_with_access(request.user, 'load', course_key)
if not notes_enabled_for_course(course): if not notes_enabled_for_course(course):
raise Http404 raise Http404
notes = Note.objects.filter(course_id=course_id, user=request.user).order_by('-created', 'uri') notes = Note.objects.filter(course_id=course_key, user=request.user).order_by('-created', 'uri')
student = request.user student = request.user
storage = course.annotation_storage_url storage = course.annotation_storage_url
...@@ -25,6 +26,7 @@ def notes(request, course_id): ...@@ -25,6 +26,7 @@ def notes(request, course_id):
'student': student, 'student': student,
'storage': storage, 'storage': storage,
'token': retrieve_token(student.email, course.annotation_token_secret), 'token': retrieve_token(student.email, course.annotation_token_secret),
'default_tab': 'myNotes',
} }
return render_to_response('notes.html', context) return render_to_response('notes.html', context)
...@@ -9,70 +9,69 @@ ...@@ -9,70 +9,69 @@
</style> </style>
<div class="annotatable-wrapper"> <div class="annotatable-wrapper">
<div class="annotatable-header"> <div class="annotatable-header">
% if display_name is not UNDEFINED and display_name is not None: % if display_name is not UNDEFINED and display_name is not None:
<div class="annotatable-title">${display_name}</div> <div class="annotatable-title">${display_name}</div>
% endif % endif
</div> </div>
% if instructions_html is not UNDEFINED and instructions_html is not None: % if instructions_html is not UNDEFINED and instructions_html is not None:
<div class="annotatable-section shaded"> <div class="annotatable-section shaded">
<div class="annotatable-section-title"> <div class="annotatable-section-title">
${_('Instructions')} ${_('Instructions')}
<a class="annotatable-toggle annotatable-toggle-instructions expanded" href="javascript:void(0)">${_('Collapse Instructions')}</a> <a class="annotatable-toggle annotatable-toggle-instructions expanded" href="javascript:void(0)">${_('Collapse Instructions')}</a>
</div> </div>
<div class="annotatable-section-body annotatable-instructions"> <div class="annotatable-section-body annotatable-instructions">
${instructions_html} ${instructions_html}
</div> </div>
</div> </div>
% endif % endif
<div class="annotatable-section"> <div class="annotatable-section">
<div class="annotatable-content"> <div class="annotatable-content">
<div id="imageHolder" class="openseadragon1"> <div id="imageHolder" class="openseadragon1">
<%namespace name='static' file='/static_content.html'/> <%namespace name='static' file='/static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)} ${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)} ${static.css(group='style-vendor-tinymce-skin', raw=True)}
</div> </div>
<div id="catchDIV"> <div id="catchDIV">
## Translators: Notes below refer to annotations. They wil later be put under a "Notes" section. <div class="annotationListContainer">${_('Note: only instructors may annotate.')}</div>
<div class="annotationListContainer">${_('You do not have any notes.')}</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<script> <script>
function onClickHideInstructions(){ function onClickHideInstructions(){
//Reset function if there is more than one event handler //Reset function if there is more than one event handler
$(this).off(); $(this).off();
$(this).on('click',onClickHideInstructions); $(this).on('click',onClickHideInstructions);
var hide = $(this).html()=='Collapse Instructions'?true:false, var hide = $(this).html()=='Collapse Instructions'?true:false,
cls, txt,slideMethod; cls, txt,slideMethod;
txt = (hide ? 'Expand' : 'Collapse') + ' Instructions'; txt = (hide ? 'Expand' : 'Collapse') + ' Instructions';
cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']); cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']);
slideMethod = (hide ? 'slideUp' : 'slideDown'); slideMethod = (hide ? 'slideUp' : 'slideDown');
$(this).text(txt).removeClass(cls[0]).addClass(cls[1]); $(this).text(txt).removeClass(cls[0]).addClass(cls[1]);
$(this).parents('.annotatable-section:first').find('.annotatable-instructions')[slideMethod](); $(this).parents('.annotatable-section:first').find('.annotatable-instructions')[slideMethod]();
} }
$('.annotatable-toggle-instructions').on('click', onClickHideInstructions); $('.annotatable-toggle-instructions').on('click', onClickHideInstructions);
//Grab uri of the course //Grab uri of the course
var parts = window.location.href.split("/"), var parts = window.location.href.split("/"),
uri = '', uri = '',
courseid; courseid;
for (var index = 0; index <= 9; index += 1) uri += parts[index]+"/"; //Get the unit url for (var index = 0; index <= 9; index += 1) uri += parts[index]+"/"; //Get the unit url
courseid = parts[4] + "/" + parts[5] + "/" + parts[6]; courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
//Change uri in cms //Change uri in cms
var lms_location = $('.sidebar .preview-button').attr('href'); var lms_location = $('.sidebar .preview-button').attr('href');
if (typeof lms_location!='undefined'){ if (typeof lms_location!='undefined'){
courseid = parts[4].split(".").join("/"); courseid = parts[4].split(".").join("/");
uri = window.location.protocol; uri = window.location.protocol;
for (var index = 0; index <= 9; index += 1) uri += lms_location.split("/")[index]+"/"; //Get the unit url 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"); var unit_id = $('#sequence-list').find('.active').attr("data-element");
uri += unit_id; uri += unit_id;
var pagination = 100, var pagination = 100,
is_staff = !('${user.is_staff}'=='False'), is_staff = ('${user.email}'=='${instructor_email}'),
options = { options = {
optionsAnnotator: { optionsAnnotator: {
permissions:{ permissions:{
...@@ -103,7 +102,7 @@ ...@@ -103,7 +102,7 @@
if (annotation.permissions) { if (annotation.permissions) {
tokens = annotation.permissions[action] || []; tokens = annotation.permissions[action] || [];
if (is_staff){ if (is_staff){
return true; return true;
} }
if (tokens.length === 0) { if (tokens.length === 0) {
return true; return true;
...@@ -153,7 +152,7 @@ ...@@ -153,7 +152,7 @@
offset:0, offset:0,
uri:uri, uri:uri,
media:'image', media:'image',
userid:'${user.email}', userid:'${user.email}',
} }
}, },
highlightTags:{ highlightTags:{
...@@ -172,6 +171,8 @@ ...@@ -172,6 +171,8 @@
}, },
optionsOpenSeadragon:{ optionsOpenSeadragon:{
id: "imageHolder", id: "imageHolder",
annotation_mode: "${annotation_mode}",
flags: is_staff,
prefixUrl: "${settings.STATIC_URL}" + "js/vendor/ova/images/", prefixUrl: "${settings.STATIC_URL}" + "js/vendor/ova/images/",
${openseadragonjson} ${openseadragonjson}
}, },
...@@ -179,29 +180,43 @@ ...@@ -179,29 +180,43 @@
}; };
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/"; var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
if (typeof Annotator != 'undefined'){ if (typeof Annotator != 'undefined'){
//remove old instances //remove old instances
if (Annotator._instances.length !== 0) { if (Annotator._instances.length !== 0) {
$('#imageHolder').annotator("destroy"); $('#imageHolder').annotator("destroy");
} }
delete osda; delete osda;
//Load the plugin Image/Text Annotation //Load the plugin Image/Text Annotation
var osda = new OpenSeadragonAnnotation($('#imageHolder'),options); var osda = new OpenSeadragonAnnotation($('#imageHolder'),options);
//Catch var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
var annotator = osda.annotator, '${instructor_email}':
catchOptions = { '${user.email}';
media:'image',
externalLink:false, //Catch
imageUrlRoot:imgURLRoot, var annotator = osda.annotator;
showMediaSelector: false, var catchOptions = {
showPublicPrivate: true, media:'image',
userId:'${user.email}', externalLink:false,
pagination:pagination,//Number of Annotations per load in the pagination, imageUrlRoot:imgURLRoot,
flags:is_staff showMediaSelector: false,
}, showPublicPrivate: true,
Catch = new CatchAnnotation($('#catchDIV'),catchOptions); userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff,
default_tab: "${default_tab}",
instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
// if annotations are opened to everyone (2) or if they want to create no annotations (1 with no instructor)
// then the table at the bottom of the source should be displayed
if ("${annotation_mode}" == "everyone" || ("${annotation_mode}" == "instructor" && "${instructor_email}" != ""))
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
// if it is in instructor mode only (1), the annotator should be destroyed for all except the instructor
if ("${annotation_mode}" == "instructor" && "${instructor_email}" == "" && !is_staff)
osda.annotator.destroy();
} }
</script> </script>
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='static_content.html'/> <%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>
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" ></script>
<%inherit file="main.html" /> <%inherit file="main.html" />
<%! <%!
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -102,7 +106,7 @@ ...@@ -102,7 +106,7 @@
if (annotation.permissions) { if (annotation.permissions) {
tokens = annotation.permissions[action] || []; tokens = annotation.permissions[action] || [];
if (is_staff){ if (is_staff){
return true; return true;
} }
if (tokens.length === 0) { if (tokens.length === 0) {
return true; return true;
...@@ -128,7 +132,7 @@ ...@@ -128,7 +132,7 @@
}, },
}, },
auth: { auth: {
tokenUrl: location.protocol+'//'+location.host+"/token?course_id=${course.id.to_deprecated_string()}" token: "${token}"
}, },
store: { store: {
// The endpoint of the store on your server. // The endpoint of the store on your server.
...@@ -158,37 +162,34 @@ ...@@ -158,37 +162,34 @@
optionsRichText: { optionsRichText: {
tinymce:{ tinymce:{
selector: "li.annotator-item textarea", selector: "li.annotator-item textarea",
plugins: "media image insertdatetime link code", plugins: "media image codemirror",
menubar: false, menubar: false,
toolbar_items_size: 'small', toolbar_items_size: 'small',
extended_valid_elements : "iframe[src|frameborder|style|scrolling|class|width|height|name|align|id]", 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 | link image media rubric | code ", toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image rubric | code ",
} }
return true;
},
},
auth: {
token: "${token}"
},
store: {
// The endpoint of the store on your server.
prefix: "${storage}",
annotationData: {},
urls: {
// These are the default URLs.
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
}, },
auth: {
token: "${token}"
},
store: {
// The endpoint of the store on your server.
prefix: "${storage}",
annotationData: {},
urls: {
// These are the default URLs.
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
},
}
}; };
tinyMCE.baseURL = "${settings.STATIC_URL}" + "js/vendor/ova";
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/"; var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
//remove old instances //remove old instances
if (Annotator._instances.length !== 0) { if (Annotator._instances.length !== 0) {
$('#notesHolder').annotator("destroy"); $('#notesHolder').annotator("destroy");
...@@ -207,7 +208,8 @@ ...@@ -207,7 +208,8 @@
showMediaSelector: true, showMediaSelector: true,
showPublicPrivate: true, showPublicPrivate: true,
pagination:pagination,//Number of Annotations per load in the pagination, pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff flags:is_staff,
default_tab: "${default_tab}",
}, },
Catch = new CatchAnnotation($('#catchDIV'),catchOptions); Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script> </script>
......
...@@ -177,17 +177,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)} ...@@ -177,17 +177,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
//Load the plugin Video/Text Annotation //Load the plugin Video/Text Annotation
var ova = new OpenVideoAnnotation.Annotator($('#textHolder'),options); var ova = new OpenVideoAnnotation.Annotator($('#textHolder'),options);
var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
'${instructor_email}':
'${user.email}';
//Catch //Catch
var annotator = ova.annotator, var annotator = ova.annotator;
catchOptions = { var catchOptions = {
media:'text', media:'text',
externalLink:false, externalLink:false,
imageUrlRoot:imgURLRoot, imageUrlRoot:imgURLRoot,
showMediaSelector: false, showMediaSelector: false,
showPublicPrivate: true, showPublicPrivate: true,
userId:'${user.email}', userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination, pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff flags:is_staff,
}, default_tab: "${default_tab}",
Catch = new CatchAnnotation($('#catchDIV'),catchOptions); instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script> </script>
...@@ -175,18 +175,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)} ...@@ -175,18 +175,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
var ova = new OpenVideoAnnotation.Annotator($('#videoHolder'),options); var ova = new OpenVideoAnnotation.Annotator($('#videoHolder'),options);
ova.annotator.addPlugin('Tags'); ova.annotator.addPlugin('Tags');
var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
'${instructor_email}':
'${user.email}';
//Catch //Catch
var annotator = ova.annotator, var annotator = ova.annotator;
catchOptions = { var catchOptions = {
media:'video', media:'video',
externalLink:false, externalLink:false,
imageUrlRoot:imgURLRoot, imageUrlRoot:imgURLRoot,
showMediaSelector: false, showMediaSelector: false,
showPublicPrivate: true, showPublicPrivate: true,
userId:'${user.email}', userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination, pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff flags:is_staff,
}, default_tab: "${default_tab}",
Catch = new CatchAnnotation($('#catchDIV'),catchOptions); instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script> </script>
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