Commit 0cb4bf66 authored by csillag's avatar csillag

First implementation of Focus

Missing: search magic
parent 7bea1bd8
...@@ -202,7 +202,7 @@ $baseFontSize: 14px; ...@@ -202,7 +202,7 @@ $baseFontSize: 14px;
//HIGHLIGHTS//////////////////////////////// //HIGHLIGHTS////////////////////////////////
.annotator-highlights-always-on .annotator-hl, .annotator-highlights-always-on .annotator-hl,
.annotator-hl-active, .annotator-hl-temporary { .annotator-hl-active, .annotator-hl-temporary, .annotator-hl-focused {
background: $highlightColor; background: $highlightColor;
box-shadow:3px 3px 4px -1px #999999; box-shadow:3px 3px 4px -1px #999999;
&::-moz-selection { &::-moz-selection {
...@@ -223,7 +223,8 @@ $baseFontSize: 14px; ...@@ -223,7 +223,8 @@ $baseFontSize: 14px;
background: $highlightModeColor; background: $highlightModeColor;
} }
.annotator-highlights-always-on .annotator-hl-active, { .annotator-highlights-always-on .annotator-hl-active,
.annotator-highlights-always-on .annotator-hl-focused {
box-shadow:3px 3px 4px 3px #999999; box-shadow:3px 3px 4px 3px #999999;
} }
......
...@@ -564,11 +564,14 @@ class Annotation ...@@ -564,11 +564,14 @@ class Annotation
switch $scope.action switch $scope.action
when 'create' when 'create'
switch # First, focus on the newly created annotation
when annotator.isComment(annotation) and unless annotator.isReply annotation
$rootScope.focus annotation, true
if annotator.isComment(annotation) and
$rootScope.viewState.view isnt "Comments" $rootScope.viewState.view isnt "Comments"
$rootScope.applyView "Comments" $rootScope.applyView "Comments"
when not annotator.isReply(annotation) and else if not annotator.isReply(annotation) and
$rootScope.viewState.view not in ["Document", "Selection"] $rootScope.viewState.view not in ["Document", "Selection"]
$rootScope.applyView "Screen" $rootScope.applyView "Screen"
annotator.publish 'annotationCreated', annotation annotator.publish 'annotationCreated', annotation
...@@ -761,7 +764,7 @@ class Viewer ...@@ -761,7 +764,7 @@ class Viewer
) -> ) ->
{providers, threading} = annotator {providers, threading} = annotator
$scope.focus = (annotation) -> $scope.activate = (annotation) ->
if angular.isArray annotation if angular.isArray annotation
highlights = (a.$$tag for a in annotation when a?) highlights = (a.$$tag for a in annotation when a?)
else if angular.isObject annotation else if angular.isObject annotation
...@@ -832,7 +835,7 @@ class Search ...@@ -832,7 +835,7 @@ class Search
result = true result = true
result result
$scope.focus = (annotation) -> $scope.activate = (annotation) ->
if angular.isArray annotation if angular.isArray annotation
highlights = (a.$$tag for a in annotation when a?) highlights = (a.$$tag for a in annotation when a?)
else if angular.isObject annotation else if angular.isObject annotation
...@@ -848,6 +851,7 @@ class Search ...@@ -848,6 +851,7 @@ class Search
# Temporary workaround, until search result annotation card # Temporary workaround, until search result annotation card
# scopes get their 'annotation' fields, too. # scopes get their 'annotation' fields, too.
return unless annotation return unless annotation
for p in providers for p in providers
p.channel.notify p.channel.notify
method: 'scrollTo' method: 'scrollTo'
......
...@@ -213,7 +213,7 @@ tabReveal = ['$parse', ($parse) -> ...@@ -213,7 +213,7 @@ tabReveal = ['$parse', ($parse) ->
] ]
thread = ['$window', ($window) -> thread = ['$rootScope', '$window', ($rootScope, $window) ->
# Helper -- true if selection ends inside the target and is non-empty # Helper -- true if selection ends inside the target and is non-empty
ignoreClick = (event) -> ignoreClick = (event) ->
sel = $window.getSelection() sel = $window.getSelection()
...@@ -225,11 +225,25 @@ thread = ['$window', ($window) -> ...@@ -225,11 +225,25 @@ thread = ['$window', ($window) ->
link: (scope, elem, attr, ctrl) -> link: (scope, elem, attr, ctrl) ->
childrenEditing = {} childrenEditing = {}
# If this is supposed to be focused, then open it
if scope.annotation in $rootScope.focused
scope.collapsed = false
scope.$on "focusChange", ->
if scope.annotation in $rootScope.focused
scope.collapsed = false
else
scope.collapsed = true
scope.toggleCollapsed = (event) -> scope.toggleCollapsed = (event) ->
event.stopPropagation() event.stopPropagation()
return if (ignoreClick event) or Object.keys(childrenEditing).length return if (ignoreClick event) or Object.keys(childrenEditing).length
scope.collapsed = !scope.collapsed scope.collapsed = !scope.collapsed
scope.openDetails scope.annotation unless scope.collapsed if scope.collapsed
$rootScope.unFocus scope.annotation, true
else
scope.openDetails scope.annotation
$rootScope.focus scope.annotation, true
scope.$on 'toggleEditing', (event) -> scope.$on 'toggleEditing', (event) ->
{$id, editing} = event.targetScope {$id, editing} = event.targetScope
......
...@@ -33,6 +33,9 @@ class Annotator.Guest extends Annotator ...@@ -33,6 +33,9 @@ class Annotator.Guest extends Annotator
# Create an array for holding the comments # Create an array for holding the comments
@comments = [] @comments = []
# These are in focus (visually marked here, and open in sidebar)
@focusedAnnotations = []
@frame = $('<div></div>') @frame = $('<div></div>')
.appendTo(@wrapper) .appendTo(@wrapper)
.addClass('annotator-frame annotator-outer annotator-collapsed') .addClass('annotator-frame annotator-outer annotator-collapsed')
...@@ -111,6 +114,18 @@ class Annotator.Guest extends Annotator ...@@ -111,6 +114,18 @@ class Annotator.Guest extends Annotator
this.publish "finalizeHighlights" this.publish "finalizeHighlights"
) )
.bind('setFocusedHighlights', (ctx, tags=[]) =>
this.focusedAnnotations = []
for hl in @getHighlights()
annotation = hl.annotation
if annotation.$$tag in tags
this.focusedAnnotations.push annotation
hl.setFocused true, true
else
hl.setFocused false, true
this.publish "finalizeHighlights"
)
.bind('scrollTo', (ctx, tag) => .bind('scrollTo', (ctx, tag) =>
for hl in @getHighlights() for hl in @getHighlights()
if hl.annotation.$$tag is tag if hl.annotation.$$tag is tag
...@@ -188,24 +203,28 @@ class Annotator.Guest extends Annotator ...@@ -188,24 +203,28 @@ class Annotator.Guest extends Annotator
_setupViewer: -> this _setupViewer: -> this
_setupEditor: -> this _setupEditor: -> this
showViewer: (viewName, annotations) => showViewer: (viewName, annotations, focused = false) =>
@panel?.notify @panel?.notify
method: "showViewer" method: "showViewer"
params: params:
view: viewName view: viewName
ids: (a.id for a in annotations) ids: (a.id for a in annotations)
focused: focused
toggleViewerSelection: (annotations) => toggleViewerSelection: (annotations, focused = false) =>
@panel?.notify @panel?.notify
method: "toggleViewerSelection" method: "toggleViewerSelection"
params: (a.id for a in annotations) params:
ids: (a.id for a in annotations)
focused: focused
updateViewer: (viewName, annotations) => updateViewer: (viewName, annotations, focused = false) =>
@panel?.notify @panel?.notify
method: "updateViewer" method: "updateViewer"
params: params:
view: viewName view: viewName
ids: (a.id for a in annotations) ids: (a.id for a in annotations)
focused: focused
showEditor: (annotation) => @plugins.Bridge.showEditor annotation showEditor: (annotation) => @plugins.Bridge.showEditor annotation
...@@ -271,36 +290,54 @@ class Annotator.Guest extends Annotator ...@@ -271,36 +290,54 @@ class Annotator.Guest extends Annotator
else else
super super
onAnchorMouseover: (event) -> # Get the list of annotations impacted by a mouse event
_getImpactedAnnotations: (event) ->
# Get the raw list
annotations = event.data.getAnnotations event
# Are the highlights supposed to be visible?
if (@tool is 'highlight') or @visibleHighlights if (@tool is 'highlight') or @visibleHighlights
this.addEmphasis event.data.getAnnotations event # Just use the whole list
annotations
else
# We need to check for focused annotations
a for a in annotations when a in this.focusedAnnotations
onAnchorMouseover: (event) ->
this.addEmphasis this._getImpactedAnnotations event
onAnchorMouseout: (event) -> onAnchorMouseout: (event) ->
if (@tool is 'highlight') or @visibleHighlights this.removeEmphasis this._getImpactedAnnotations event
this.removeEmphasis event.data.getAnnotations event
# When clicking on a highlight in highlighting mode, # When clicking on a highlight in highlighting mode,
# set @noBack to true to prevent the sidebar from closing # set @noBack to true to prevent the sidebar from closing
onAnchorMousedown: (event) => onAnchorMousedown: (event) =>
if (@tool is 'highlight') or @visibleHighlights if this._getImpactedAnnotations(event).length
@noBack = true @noBack = true
# When clicking on a highlight in highlighting mode, # Select some annotations.
# tell the sidebar to bring up the viewer for the relevant annotations #
onAnchorClick: (event) => # toggle: should this toggle membership in an existing selection?
return unless (@tool is 'highlight') or @visibleHighlights and @noBack # focus: should these annotation become focused?
selectAnnotations: (annotations, toggle, focus) =>
# Switch off dynamic mode; we are going to "Selection" scope # Switch off dynamic mode; we are going to "Selection" scope
@plugins.Heatmap.dynamicBucket = false @plugins.Heatmap.dynamicBucket = false
annotations = event.data.getAnnotations event if toggle
if event.metaKey or event.ctrlKey
# Tell sidebar to add these annotations to the sidebar # Tell sidebar to add these annotations to the sidebar
this.toggleViewerSelection annotations this.toggleViewerSelection annotations, focus
else else
# Tell sidebar to show the viewer for these annotations # Tell sidebar to show the viewer for these annotations
this.showViewer "Selection", annotations this.showViewer "Selection", annotations, focus
# When clicking on a highlight in highlighting mode,
# tell the sidebar to bring up the viewer for the relevant annotations
onAnchorClick: (event) =>
annotations = this._getImpactedAnnotations event
return unless annotations.length and @noBack
this.selectAnnotations annotations,
(event.metaKey or event.ctrlKey), true
# We have already prevented closing the sidebar, now reset this flag # We have already prevented closing the sidebar, now reset this flag
@noBack = false @noBack = false
......
...@@ -470,11 +470,8 @@ class Annotator.Plugin.Heatmap extends Annotator.Plugin ...@@ -470,11 +470,8 @@ class Annotator.Plugin.Heatmap extends Annotator.Plugin
@commentClick() @commentClick()
else else
d3.event.stopPropagation() d3.event.stopPropagation()
@dynamicBucket = false annotator.selectAnnotations @buckets[bucket].slice(),
if d3.event.ctrlKey or d3.event.metaKey (d3.event.ctrlKey or d3.event.metaKey), true
annotator.toggleViewerSelection @buckets[bucket]
else
annotator.showViewer "Selection", @buckets[bucket]
tabs.exit().remove() tabs.exit().remove()
......
...@@ -173,6 +173,25 @@ class Hypothesis extends Annotator ...@@ -173,6 +173,25 @@ class Hypothesis extends Annotator
# Track the visible annotations in the root scope # Track the visible annotations in the root scope
$rootScope.annotations = [] $rootScope.annotations = []
$rootScope.search_annotations = [] $rootScope.search_annotations = []
$rootScope.focused = []
$rootScope.focus = (annotation, announce = false) =>
unless annotation
console.log "Warning: trying to focus on null annotation"
return
return if annotation in $rootScope.focused
$rootScope.focused.push annotation
this._broadcastFocusInfo() if announce
$rootScope.unFocus = (annotation, announce = false) =>
index = $rootScope.focused.indexOf annotation
return if index is -1
$rootScope.focused.splice index, 1
this._broadcastFocusInfo() if announce
# Add new annotations to the view when they are created # Add new annotations to the view when they are created
this.subscribe 'annotationCreated', (a) => this.subscribe 'annotationCreated', (a) =>
...@@ -184,6 +203,13 @@ class Hypothesis extends Annotator ...@@ -184,6 +203,13 @@ class Hypothesis extends Annotator
$rootScope.annotations = $rootScope.annotations.filter (b) -> b isnt a $rootScope.annotations = $rootScope.annotations.filter (b) -> b isnt a
$rootScope.search_annotations = $rootScope.search_annotations.filter (b) -> b.message? $rootScope.search_annotations = $rootScope.search_annotations.filter (b) -> b.message?
_broadcastFocusInfo: ->
$rootScope = @element.injector().get '$rootScope'
for p in @providers
p.channel.notify
method: 'setFocusedHighlights'
params: (a.$$tag for a in $rootScope.focused)
_setupXDM: (options) -> _setupXDM: (options) ->
$rootScope = @element.injector().get '$rootScope' $rootScope = @element.injector().get '$rootScope'
...@@ -216,22 +242,22 @@ class Hypothesis extends Annotator ...@@ -216,22 +242,22 @@ class Hypothesis extends Annotator
$rootScope.$apply => this.show() $rootScope.$apply => this.show()
) )
.bind('showViewer', (ctx, {view, ids}) => .bind('showViewer', (ctx, {view, ids, focused}) =>
ids ?= [] ids ?= []
return unless this.discardDrafts() return unless this.discardDrafts()
$rootScope.$apply => $rootScope.$apply =>
this.showViewer view, this._getAnnotationsFromIDs ids this.showViewer view, this._getAnnotationsFromIDs(ids), focused
) )
.bind('updateViewer', (ctx, {view, ids}) => .bind('updateViewer', (ctx, {view, ids, focused}) =>
ids ?= [] ids ?= []
$rootScope.$apply => $rootScope.$apply =>
this.updateViewer view, this._getAnnotationsFromIDs ids this.updateViewer view, this._getAnnotationsFromIDs(ids), focused
) )
.bind('toggleViewerSelection', (ctx, ids=[]) => .bind('toggleViewerSelection', (ctx, {ids, focused}) =>
$rootScope.$apply => $rootScope.$apply =>
this.toggleViewerSelection this._getAnnotationsFromIDs ids this.toggleViewerSelection this._getAnnotationsFromIDs(ids), focused
) )
.bind('setTool', (ctx, name) => .bind('setTool', (ctx, name) =>
...@@ -322,12 +348,12 @@ class Hypothesis extends Annotator ...@@ -322,12 +348,12 @@ class Hypothesis extends Annotator
annotation.reply_list = children.sort(@sortAnnotations).reverse() annotation.reply_list = children.sort(@sortAnnotations).reverse()
@buildReplyList children @buildReplyList children
toggleViewerSelection: (annotations=[]) => toggleViewerSelection: (annotations=[], focused) =>
annotations = annotations.filter (a) -> a? annotations = annotations.filter (a) -> a?
@element.injector().invoke [ @element.injector().invoke [
'$rootScope', '$rootScope',
($rootScope) => ($rootScope) =>
if $rootScope.view is "Selection" if $rootScope.viewState.view is "Selection"
# We are already in selection mode; just XOR this list # We are already in selection mode; just XOR this list
# to the current selection # to the current selection
@buildReplyList annotations @buildReplyList annotations
...@@ -336,36 +362,61 @@ class Hypothesis extends Annotator ...@@ -336,36 +362,61 @@ class Hypothesis extends Annotator
index = list.indexOf a index = list.indexOf a
if index isnt -1 if index isnt -1
list.splice index, 1 list.splice index, 1
$rootScope.unFocus a, true
else else
list.push a list.push a
if focused
$rootScope.focus a, true
else else
# We are not in selection mode, # We are not in selection mode,
# so we switch to it, and make this list # so we switch to it, and make this list
# the new selection # the new selection
$rootScope.view = "Selection" $rootScope.viewState.view = "Selection"
$rootScope.annotations = annotations $rootScope.annotations = annotations
] ]
this this
updateViewer: (viewName, annotations=[]) => updateViewer: (viewName, annotations=[], focused = false) =>
annotations = annotations.filter (a) -> a? annotations = annotations.filter (a) -> a?
@element.injector().invoke [ @element.injector().invoke [
'$rootScope', '$rootScope',
($rootScope) => ($rootScope) =>
@buildReplyList annotations @buildReplyList annotations
# Do we have to replace the focused list with this?
if focused
# Nuke the old focus list
$rootScope.focused = []
# Add the new elements
for a in annotations
$rootScope.focus a, true
else
# Go over the old list, and unfocus the ones
# that are not on this list
for a in $rootScope.focused.slice() when a not in annotations
$rootScope.unFocus a, true
# Update the main annotations list
$rootScope.annotations = annotations $rootScope.annotations = annotations
$rootScope.applyView viewName
# Announce focus changes
$rootScope.$broadcast 'focusChange'
unless $rootScope.viewState.view is viewName
# We are changing the view
$rootScope.viewState.view = viewName
$rootScope.showViewSort true, true
] ]
this this
showViewer: (viewName, annotations=[]) => showViewer: (viewName, annotations=[], focused = false) =>
this.show() this.show()
@element.injector().invoke [ @element.injector().invoke [
'$location', '$location',
($location) => ($location) =>
$location.path('/viewer').replace() $location.path('/viewer').replace()
] ]
this.updateViewer viewName, annotations this.updateViewer viewName, annotations, focused
addEmphasis: (annotations=[]) => addEmphasis: (annotations=[]) =>
annotations = annotations.filter (a) -> a? # Filter out null annotations annotations = annotations.filter (a) -> a? # Filter out null annotations
......
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