Skip to content
Snippets Groups Projects
Commit 48bb447f authored by Arthur Barrett's avatar Arthur Barrett
Browse files

Adding basic annotatable module and related files.

parent 7a8e87c7
Branches
Tags
No related merge requests found
......@@ -40,7 +40,8 @@ setup(
"static_tab = xmodule.html_module:StaticTabDescriptor",
"custom_tag_template = xmodule.raw_module:RawDescriptor",
"about = xmodule.html_module:AboutDescriptor",
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor"
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor",
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor"
]
}
)
import json
import logging
import re
from lxml import etree
from pkg_resources import resource_string, resource_listdir
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.content import StaticContent
import datetime
import time
log = logging.getLogger(__name__)
class AnnotatableModule(XModule):
# Note: js and css in common/lib/xmodule/xmodule
js = {'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee'),
resource_string(__name__, 'js/src/collapsible.coffee'),
resource_string(__name__, 'js/src/html/display.coffee'),
resource_string(__name__, 'js/src/annotatable/display.coffee')],
'js': []
}
js_module_name = "Annotatable"
css = {'scss': [resource_string(__name__, 'css/annotatable/display.scss')]}
def _is_span(self, element):
""" Returns true if the element is a valid annotation span, false otherwise. """
return element.tag == 'span' and element.get('class') == 'annotatable'
def _is_span_container(self, element):
""" Returns true if the element is a valid span contanier, false otherwise. """
return element.tag == 'p' # Assume content is in paragraph form (for now...)
def _iterspans(self, xmltree, callbacks):
""" Iterates over span elements and invokes each callback on the span. """
index = 0
for element in xmltree.iter('span'):
if self._is_span(element):
for callback in callbacks:
callback(element, index, xmltree)
index += 1
def _get_span_container(self, span):
""" Returns the first container element of the span.
The intent is to add the discussion widgets at the
end of the container, not interspersed with the text. """
container = None
for parent in span.iterancestors():
if self._is_span_container(parent):
container = parent
break
if container is None:
return parent
return container
def _attach_discussion(self, span, index, xmltree):
""" Attaches a discussion thread to the annotation span. """
tpl = u'<div class="annotatable-discussion" data-discussion-id="{0}">'
tpl += '<div class="annotatable-icon"> </div>'
tpl += '<span class="annotatable-discussion-label">Guided Discussion: </span>'
tpl += '<span class="annotatable-discussion-thread">{1}</span>'
tpl += '<a class="annotatable-show-discussion" href="javascript:void(0);">Show Discussion</a>'
tpl += '</div>'
span_id = 'span-{0}'.format(index) # How should we anchor spans?
span.set('data-span-id', span_id)
discussion_id = 'discussion-{0}'.format(index) # How do we get a real discussion ID?
discussion_title = 'Thread Title {0}'.format(index) # How do we get the discussion Title?
discussion_html = tpl.format(discussion_id, discussion_title)
discussion = etree.fromstring(discussion_html)
span_container = self._get_span_container(span)
span_container.append(discussion)
self.discussion_for[span_id] = discussion_id
def _add_icon(self, span, index, xmltree):
""" Adds an icon to the annotation span. """
span_icon = etree.Element('span', { 'class': 'annotatable-icon'} )
span_icon.text = '';
span_icon.tail = span.text
span.text = ''
span.insert(0, span_icon)
def _render(self):
""" Renders annotatable content by transforming spans and adding discussions. """
xmltree = etree.fromstring(self.content)
self._iterspans(xmltree, [ self._add_icon, self._attach_discussion ])
return etree.tostring(xmltree)
def get_html(self):
""" Renders parameters to template. """
context = {
'display_name': self.display_name,
'element_id': self.element_id,
'html_content': self._render(),
'json_discussion_for': json.dumps(self.discussion_for)
}
# template dir: lms/templates
return self.system.render_template('annotatable.html', context)
def __init__(self, system, location, definition, descriptor,
instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, descriptor,
instance_state, shared_state, **kwargs)
self.element_id = self.location.html_id();
self.content = self.definition['data']
self.discussion_for = {} # Maps spans to discussions by id (for JS)
class AnnotatableDescriptor(RawDescriptor):
module_class = AnnotatableModule
stores_state = True
template_dir_name = "annotatable"
.annotatable-header {
border: 1px solid $border-color;
border-radius: 3px;
margin-bottom: 1em;
padding: 2px 4px;
position: relative;
.annotatable-title {
font-size: em(18);
text-transform: uppercase;
}
.annotatable-description {
font-size: $body-font-size;
}
}
span.annotatable {
color: $blue;
.annotatable-icon {
margin: auto 2px auto 4px;
}
}
.annotatable-icon {
display: inline-block;
vertical-align: middle;
width: 16px;
height: 17px;
background: url(../images/link-icon.png) no-repeat;
}
.help-icon {
display: block;
position: absolute;
right: 0;
top: 33%;
width: 16px;
height: 17px;
margin: 0 7px 0 0;
background: url(../images/info-icon.png) no-repeat;
}
.annotatable-discussion {
display: block;
border: 1px solid $border-color;
border-radius: 3px;
margin: 1em 0;
position: relative;
padding: 4px;
.annotatable-discussion-label {
font-weight: bold;
}
.annotatable-icon {
margin: auto 4px auto 0px;
}
.annotatable-show-discussion {
position: absolute;
right: 8px;
margin-top: 4px;
}
}
class @Annotatable
constructor: (el) ->
console.log "loaded Annotatable"
$(el).find(".annotatable").on "click", (e) ->
data = $(".annotatable-wrapper", el).data("spans")
span_id = e.target.getAttribute("data-span-id")
msg = "annotatable span clicked. discuss span [" + span_id + "] in discussion [" + data[span_id] + "]"
console.log data
window.alert msg
<div class="annotatable-wrapper" id="${element_id}">
<div class="annotatable-header">
<div class="help-icon"></div>
% if display_name is not UNDEFINED and display_name is not None:
<div class="annotatable-title">${display_name} </div>
% endif
<div class="annotatable-description">Annotated Reading + Guided Discussion</div>
</div>
<div class="annotatable-content">
${html_content}
</div>
<script>
$(function() {
// TODO pass spans to module directly
var el = $('#${element_id}.annotatable-wrapper');
el.data('spans', ${json_discussion_for});
});
</script>
</div>
\ No newline at end of file
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