From a9773eb888c0c52dd93f92aed5122c86b7d13548 Mon Sep 17 00:00:00 2001 From: Arthur Barrett Date: Fri, 8 Feb 2013 20:38:24 -0500 Subject: [PATCH] Updated markup, styling, and tooltip behavior for displaying instructor comments. Added drag/drop to the comments so the user can organize them as they wish after clicking on the span. When the user hides the annotations, their positions on the screen are remembered so they can be restored later. Also modified the markup so that block content can be displayed. --- .../lib/xmodule/xmodule/annotatable_module.py | 18 +-- .../xmodule/css/annotatable/display.scss | 23 ++-- .../xmodule/js/src/annotatable/display.coffee | 113 +++++++++++++----- lms/templates/annotatable.html | 2 +- 4 files changed, 108 insertions(+), 48 deletions(-) diff --git a/common/lib/xmodule/xmodule/annotatable_module.py b/common/lib/xmodule/xmodule/annotatable_module.py index 5b3cc15c77..c2fd973918 100644 --- a/common/lib/xmodule/xmodule/annotatable_module.py +++ b/common/lib/xmodule/xmodule/annotatable_module.py @@ -30,20 +30,20 @@ class AnnotatableModule(XModule): 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' + return element.get('class') == 'annotatable' def _iterspans(self, xmltree, callbacks): - """ Iterates over span elements and invokes each callback on the span. """ + """ Iterates over elements and invokes each callback on the span. """ index = 0 - for element in xmltree.iter('span'): + for element in xmltree.iter(): if self._is_span(element): for callback in callbacks: callback(element, index, xmltree) index += 1 def _set_span_data(self, span, index, xmltree): - """ Sets an ID and discussion anchor for the span. """ + """ Sets the discussion anchor for the span. """ if 'anchor' in span.attrib: span.set('data-discussion-anchor', span.get('anchor')) @@ -52,13 +52,12 @@ class AnnotatableModule(XModule): def _decorate_span(self, span, index, xmltree): """ Decorates the span with an icon and highlight. """ - cls = ['annotatable', ] + cls = ['annotatable-span', 'highlight'] marker = self._get_marker_color(span) - if marker is None: - cls.append('highlight-yellow') - else: + if marker is not None: cls.append('highlight-'+marker) + span.tag = 'div' span.set('class', ' '.join(cls)) span_icon = etree.Element('span', { 'class': 'annotatable-icon'} ) span_icon.text = ''; @@ -76,9 +75,12 @@ class AnnotatableModule(XModule): break if comment is not None: + comment.tag = 'div' comment.set('class', 'annotatable-comment') def _get_marker_color(self, span): + """ Returns the name of the marker color for the span if it is valid, otherwise none.""" + valid_markers = ['yellow', 'orange', 'purple', 'blue', 'green'] if 'marker' in span.attrib: marker = span.attrib['marker'] diff --git a/common/lib/xmodule/xmodule/css/annotatable/display.scss b/common/lib/xmodule/xmodule/css/annotatable/display.scss index d1f39332f6..a8024a6d14 100644 --- a/common/lib/xmodule/xmodule/css/annotatable/display.scss +++ b/common/lib/xmodule/xmodule/css/annotatable/display.scss @@ -19,7 +19,8 @@ font-size: $body-font-size; } -span.annotatable { +.annotatable-span { + display: inline; cursor: pointer; @each $highlight in ( (yellow rgb(239, 255, 0)), @@ -27,9 +28,10 @@ span.annotatable { (purple rgb(255,0,197)), (blue rgb(0,90,255)), (green rgb(111,255,9))) { - &.highlight-#{nth($highlight,1)} { - background-color: #{lighten(nth($highlight,2), 20%)}; - } + $marker: nth($highlight,1); + $color: lighten(nth($highlight,2), 20%); + @if $marker == yellow { &.highlight { background-color: $color; } } + &.highlight-#{$marker} { background-color: $color; } } &.hide { cursor: none; @@ -50,7 +52,7 @@ span.annotatable { .annotatable-icon { display: inline-block; vertical-align: middle; - width: 16px; + width: 17px; height: 17px; background: url(../images/link-icon.png) no-repeat; } @@ -60,12 +62,12 @@ span.annotatable { margin: 1em 0 .5em 0; } -.help-icon { +.annotatable-help-icon { display: block; position: absolute; right: 0; top: 33%; - width: 16px; + width: 17px; height: 17px; margin: 0 7px 0 0; background: url(../images/info-icon.png) no-repeat; @@ -80,11 +82,14 @@ span.annotatable { background: rgba(255, 255, 255, 0.9); border: 1px solid $border-color; color: #000; + font-weight: normal; margin-bottom: 6px; margin-right: 0; - overflow: visible; padding: 4px; text-align: left; + max-width: 300px; + max-height: 300px; + overflow: auto; -webkit-font-smoothing: antialiased; } -} \ No newline at end of file +} diff --git a/common/lib/xmodule/xmodule/js/src/annotatable/display.coffee b/common/lib/xmodule/xmodule/js/src/annotatable/display.coffee index 45cbb20bec..a021b3e9d8 100644 --- a/common/lib/xmodule/xmodule/js/src/annotatable/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/annotatable/display.coffee @@ -3,64 +3,89 @@ class @Annotatable wrapperSelector: '.annotatable-wrapper' toggleSelector: '.annotatable-toggle' - spanSelector: 'span.annotatable' + spanSelector: '.annotatable-span' commentSelector: '.annotatable-comment' - replySelector: 'a.annotatable-reply' + replySelector: '.annotatable-reply' + helpSelector: '.annotatable-help-icon' constructor: (el) -> console.log 'loaded Annotatable' if @_debug - @init(el) + @el = el + @init() $: (selector) -> $(selector, @el) - init: (el) -> - @el = el - @hideAnnotations = false + init: () -> @initEvents() - @initToolTips() + @initTips() initEvents: () -> + @annotationsHidden = false @$(@toggleSelector).bind 'click', @onClickToggleAnnotations @$(@wrapperSelector).delegate @replySelector, 'click', @onClickReply - - initToolTips: () -> + + initTips: () -> + @visibleTips = [] @$(@spanSelector).each (index, el) => $(el).qtip(@getTipOptions el) + @$(@helpSelector).qtip + position: + my: 'right top' + at: 'bottom left' + content: + title: 'Annotated Reading Help' + text: "To reveal annotations in the reading, click the highlighted areas. + Discuss the annotations in the forums using the reply link at the + end of the annotation.

+ To conceal annotations, use the Hide Annotations button." + getTipOptions: (el) -> content: - title: + title: text: @makeTipTitle(el) button: 'Close' - text: @makeTipComment(el) + text: @makeTipContent(el) position: my: 'bottom center' # of tooltip at: 'top center' # of target target: 'mouse' container: @$(@wrapperSelector) - adjust: + adjust: mouse: false # dont follow the mouse - method: 'shift none' - show: + show: event: 'click' hide: event: 'click' style: classes: 'ui-tooltip-annotatable' events: - show: @onShowTipComment + render: @onRenderTip + show: @onShowTip - onShowTipComment: (event, api) => - event.preventDefault() if @hideAnnotations + onRenderTip: (event, api) => + $(api.elements.tooltip).draggable + handle: '.ui-tooltip-title' + cursor: 'move' + + onShowTip: (event, api) => + event.preventDefault() if @annotationsHidden onClickToggleAnnotations: (e) => - @hideAnnotations = !@hideAnnotations - hide = @hideAnnotations + toggle = @$(@toggleSelector) + spans = @$(@spanSelector) - @hideAllTips() if hide - @$(@spanSelector).toggleClass('hide', hide) - @$(@toggleSelector).text((if hide then 'Show' else 'Hide') + ' Annotations') + @annotationsHidden = !@annotationsHidden + if @annotationsHidden + spans.toggleClass('hide', true) + toggle.text('Show Annotations') + @visibleTips = @getVisibleTips() + @hideTips(@visibleTips) + else + spans.toggleClass('hide', false) + toggle.text('Hide Annotations') + @showTips(@visibleTips) onClickReply: (e) => hash = $(e.currentTarget).attr('href') @@ -70,11 +95,16 @@ class @Annotatable @scrollTo(anchor) if anchor.length == 1 scrollTo: (el, padding = 20) -> - scrollTop = el.offset().top - padding - $('html,body').animate(scrollTop: scrollTop, 500, 'swing') + props = + scrollTop: (el.offset().top - padding) + opts = + duration: 500 + complete: @_once -> el.effect 'highlight', {}, 2000 - makeTipComment: (el) -> - return (api) => + $('html,body').animate(props, opts) + + makeTipContent: (el) -> + (api) => comment = $(@commentSelector, el).first().clone() anchor = $(el).data('discussion-anchor') if anchor @@ -82,13 +112,36 @@ class @Annotatable comment.contents() makeTipTitle: (el) -> - return (api) => + (api) => comment = $(@commentSelector, el).first() title = comment.attr('title') (if title then title else 'Commentary') createReplyLink: (anchor) -> - $("Reply to Comment") + $("Reply to this comment") + + getVisibleTips: () -> + visible = [] + @$(@spanSelector).each (index, el) -> + api = $(el).qtip('api') + tip = $(api?.elements.tooltip) + if tip.is(':visible') + visible.push [el, tip.offset()] + visible - hideAllTips: () -> - @$(@spanSelector).each (index, el) -> $(el).qtip('api').hide() \ No newline at end of file + hideTips: (items) -> + elements = (pair[0] for pair in items) + $(elements).qtip('hide') + + showTips: (items) -> + $.each items, (index, item) -> + [el, offset] = item + api = $(el).qtip('api') + api?.show() + $(api?.elements.tooltip).offset(offset) + + _once: (fn) -> + done = false + return => + fn.call this unless done + done = true diff --git a/lms/templates/annotatable.html b/lms/templates/annotatable.html index 5f60c6cba2..1cb40a0068 100644 --- a/lms/templates/annotatable.html +++ b/lms/templates/annotatable.html @@ -1,6 +1,6 @@
-
+
% if display_name is not UNDEFINED and display_name is not None:
${display_name}
% endif