Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
5279e095
Commit
5279e095
authored
Jan 17, 2015
by
Nick Stenning
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1855 from hypothesis/store-resource
Introduce Store resource
parents
5964631d
732f11a8
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
347 additions
and
617 deletions
+347
-617
app.coffee
h/static/scripts/app.coffee
+3
-13
auth-service.coffee
h/static/scripts/auth-service.coffee
+4
-3
controllers.coffee
h/static/scripts/controllers.coffee
+77
-118
annotation.coffee
h/static/scripts/directives/annotation.coffee
+9
-17
permissions-service.coffee
h/static/scripts/permissions-service.coffee
+0
-1
discovery.coffee
h/static/scripts/plugin/discovery.coffee
+0
-29
threading.coffee
h/static/scripts/plugin/threading.coffee
+23
-4
services.coffee
h/static/scripts/services.coffee
+32
-96
store-service.coffee
h/static/scripts/store-service.coffee
+40
-0
streamer-service.coffee
h/static/scripts/streamer-service.coffee
+1
-2
streamsearch.coffee
h/static/scripts/streamsearch.coffee
+10
-10
annotator.store.js
h/static/scripts/vendor/annotator.store.js
+0
-296
karma.config.js
karma.config.js
+0
-1
auth-service-test.coffee
tests/js/auth-service-test.coffee
+33
-15
controllers-test.coffee
tests/js/controllers-test.coffee
+6
-0
annotation-test.coffee
tests/js/directives/annotation-test.coffee
+35
-5
privacy-test.coffee
tests/js/directives/privacy-test.coffee
+5
-7
store-service-test.coffee
tests/js/store-service-test.coffee
+68
-0
streamer-service-test.coffee
tests/js/streamer-service-test.coffee
+1
-0
No files found.
h/static/scripts/app.coffee
View file @
5279e095
...
...
@@ -12,22 +12,12 @@ imports = [
resolve
=
storeConfig
:
[
'$q'
,
'annotator'
,
(
$q
,
annotator
)
->
if
annotator
.
plugins
.
Store
then
return
storeReady
=
$q
.
defer
()
resolve
=
(
options
)
->
annotator
.
options
.
Store
?=
{}
angular
.
extend
annotator
.
options
.
Store
,
options
storeReady
.
resolve
()
annotator
.
subscribe
'serviceDiscovery'
,
resolve
storeReady
.
promise
.
finally
->
annotator
.
unsubscribe
'serviceDiscovery'
,
resolve
]
store
:
[
'store'
,
(
store
)
->
store
.
$promise
]
configure
=
[
'$locationProvider'
,
'$routeProvider'
,
'$sceDelegateProvider'
,
'streamerProvider'
,
(
$locationProvider
,
$routeProvider
,
$sceDelegateProvider
,
streamerProvider
)
->
'$locationProvider'
,
'$routeProvider'
,
'$sceDelegateProvider'
,
(
$locationProvider
,
$routeProvider
,
$sceDelegateProvider
)
->
$locationProvider
.
html5Mode
(
true
)
$routeProvider
.
when
'/a/:id'
,
...
...
h/static/scripts/auth-service.coffee
View file @
5279e095
...
...
@@ -10,9 +10,9 @@
###
class
Auth
this
.
$inject
=
[
'$location'
,
'$rootScope'
,
this
.
$inject
=
[
'$
http'
,
'$
location'
,
'$rootScope'
,
'annotator'
,
'documentHelpers'
,
'identity'
]
constructor
:
(
$location
,
$rootScope
,
constructor
:
(
$
http
,
$
location
,
$rootScope
,
annotator
,
documentHelpers
,
identity
)
->
{
plugins
}
=
annotator
_checkingToken
=
false
...
...
@@ -34,15 +34,16 @@ class Auth
plugins
.
Auth
.
withToken
(
token
)
=>
_checkingToken
=
false
@
user
=
token
.
userId
$http
.
defaults
.
headers
.
common
[
'X-Annotator-Auth-Token'
]
=
assertion
$rootScope
.
$apply
()
# Fired when the identity-service forgets authentication.
# Destroys the Annotator.Auth plugin instance and sets
# the user to null.
onlogout
=
=>
plugins
.
Auth
?
.
element
.
removeData
(
'annotator:headers'
)
plugins
.
Auth
?
.
destroy
()
delete
plugins
.
Auth
delete
$http
.
defaults
.
headers
.
common
[
'X-Annotator-Auth-Token'
]
@
user
=
null
_checkingToken
=
false
...
...
h/static/scripts/controllers.coffee
View file @
5279e095
class
AppController
this
.
$inject
=
[
'$location'
,
'$route'
,
'$scope'
,
'$
timeout'
,
'$
window'
,
'annotator'
,
'auth'
,
'documentHelpers'
,
'drafts'
,
'
flash'
,
'
identity'
,
'$location'
,
'$route'
,
'$scope'
,
'$window'
,
'annotator'
,
'auth'
,
'documentHelpers'
,
'drafts'
,
'identity'
,
'permissions'
,
'streamer'
,
'streamfilter'
]
constructor
:
(
$location
,
$route
,
$scope
,
$
timeout
,
$
window
,
annotator
,
auth
,
documentHelpers
,
drafts
,
flash
,
identity
,
$location
,
$route
,
$scope
,
$window
,
annotator
,
auth
,
documentHelpers
,
drafts
,
identity
,
permissions
,
streamer
,
streamfilter
,
)
->
...
...
@@ -21,106 +21,33 @@ class AppController
return
unless
data
?
.
length
switch
action
when
'create'
,
'update'
,
'past'
plugins
.
Store
?
.
_onL
oadAnnotations
data
annotator
.
l
oadAnnotations
data
when
'delete'
for
annotation
in
data
annotation
=
plugins
.
Threading
.
idTable
[
annotation
.
id
]
?
.
message
continue
unless
annotation
?
plugins
.
Store
?
.
unregisterAnnotation
(
annotation
)
annotator
.
deleteAnnotation
(
annotation
)
annotator
.
publish
'annotationDeleted'
,
(
annotation
)
streamer
.
onmessage
=
(
data
)
->
if
!
data
or
data
.
type
!=
'annotation-notification'
return
return
if
!
data
or
data
.
type
!=
'annotation-notification'
action
=
data
.
options
.
action
payload
=
data
.
payload
if
$scope
.
socialView
.
name
is
'single-player'
payload
=
payload
.
filter
(
ann
)
->
ann
.
user
is
auth
.
user
applyUpdates
(
action
,
payload
)
$scope
.
$digest
()
initStore
=
->
# Initialize the storage component.
Store
=
plugins
.
Store
delete
plugins
.
Store
if
auth
.
user
or
annotator
.
socialView
.
name
is
'none'
annotator
.
addPlugin
'Store'
,
annotator
.
options
.
Store
$scope
.
store
=
plugins
.
Store
return
unless
Store
Store
.
destroy
()
# XXX: Hacky hacky stuff to ensure that any search requests in-flight
# at this time have no effect when they resolve and that future events
# have no effect on this Store. Unfortunately, it's not possible to
# unregister all the events or properly unload the Store because the
# registration loses the closure. The approach here is perhaps
# cleaner than fishing them out of the jQuery private data.
# * Overwrite the Store's handle to the annotator, giving it one
# with a noop `loadAnnotations` method.
Store
.
annotator
=
loadAnnotations
:
angular
.
noop
# * Make all api requests into a noop.
Store
.
_apiRequest
=
angular
.
noop
# * Ignore pending searches
Store
.
_onLoadAnnotations
=
angular
.
noop
# * Make the update function into a noop.
Store
.
updateAnnotation
=
angular
.
noop
# Sort out which annotations should remain in place.
user
=
auth
.
user
view
=
annotator
.
socialView
.
name
cull
=
(
acc
,
annotation
)
->
if
view
is
'single-player'
and
annotation
.
user
!=
user
acc
.
drop
.
push
annotation
else
if
permissions
.
permits
(
'read'
,
annotation
,
user
)
acc
.
keep
.
push
annotation
else
acc
.
drop
.
push
annotation
acc
{
keep
,
drop
}
=
Store
.
annotations
.
reduce
cull
,
{
keep
:
[],
drop
:
[]}
Store
.
annotations
=
[]
if
plugins
.
Store
?
plugins
.
Store
.
annotations
=
keep
else
drop
=
drop
.
concat
keep
# Clean up the ones that should be removed.
do
cleanup
=
(
drop
)
->
return
if
drop
.
length
==
0
[
first
,
rest
...]
=
drop
annotator
.
deleteAnnotation
first
$timeout
->
cleanup
rest
oncancel
=
->
$scope
.
dialog
.
visible
=
false
reset
=
->
$scope
.
dialog
.
visible
=
false
# Update any edits in progress.
for
draft
in
drafts
.
all
()
annotator
.
publish
'beforeAnnotationCreated'
,
draft
# Reload services
initStore
()
streamer
.
close
()
streamer
.
open
(
$window
.
WebSocket
,
streamerUrl
)
$scope
.
$watch
'socialView.name'
,
(
newValue
,
oldValue
)
->
return
if
newValue
is
oldValue
initStore
()
if
newValue
is
'single-player'
and
not
auth
.
user
annotator
.
show
()
flash
'info'
,
'You will need to sign in for your highlights to be saved.'
$scope
.
$on
'$routeChangeStart'
,
(
event
,
newRoute
,
oldRoute
)
->
return
if
newRoute
.
redirectTo
# Clean up any annotations that need to be unloaded.
for
id
,
container
of
$scope
.
threading
.
idTable
when
container
.
message
# Remove annotations not belonging to this user when highlighting.
if
annotator
.
tool
is
'highlight'
and
annotation
.
user
!=
auth
.
user
annotator
.
publish
'annotationDeleted'
,
container
.
message
drafts
.
remove
annotation
# Remove annotations the user is not authorized to view.
else
if
not
permissions
.
permits
'read'
,
container
.
message
,
auth
.
user
annotator
.
publish
'annotationDeleted'
,
container
.
message
drafts
.
remove
container
.
message
$scope
.
$watch
'sort.name'
,
(
name
)
->
return
unless
name
...
...
@@ -130,20 +57,27 @@ class AppController
when
'Location'
then
[
'-!!message'
,
'message.target[0].pos.top'
]
$scope
.
sort
=
{
name
,
predicate
}
$scope
.
$watch
'store.entities'
,
(
entities
,
oldEntities
)
->
return
if
entities
is
oldEntities
$scope
.
$watch
(
->
auth
.
user
),
(
newVal
,
oldVal
)
->
return
if
newVal
is
oldVal
if
isFirstRun
and
not
(
newVal
or
oldVal
)
$scope
.
login
()
else
$scope
.
dialog
.
visible
=
false
# Skip the remaining if this is the first evaluation.
return
if
oldVal
is
undefined
if
entities
.
length
streamfilter
.
resetFilter
()
.
addClause
(
'/uri'
,
'one_of'
,
entities
)
# Update any edits in progress.
for
draft
in
drafts
.
all
()
annotator
.
publish
'beforeAnnotationCreated'
,
draft
streamer
.
send
({
filter
:
streamfilter
.
getFilter
()})
# Reopen the streamer.
streamer
.
close
()
streamer
.
open
(
$window
.
WebSocket
,
streamerUrl
)
$scope
.
$watch
'auth.user'
,
(
newVal
,
oldVal
)
->
return
if
newVal
is
undefined
reset
()
$scope
.
login
()
if
isFirstRun
and
not
(
newVal
or
oldVal
)
# Reload the view.
$route
.
reload
()
$scope
.
login
=
->
$scope
.
dialog
.
visible
=
true
...
...
@@ -177,7 +111,6 @@ class AppController
delete
$scope
.
selectedAnnotations
delete
$scope
.
selectedAnnotationsCount
$scope
.
socialView
=
annotator
.
socialView
$scope
.
sort
=
name
:
'Location'
$scope
.
threading
=
plugins
.
Threading
...
...
@@ -185,21 +118,16 @@ class AppController
class
AnnotationViewerController
this
.
$inject
=
[
'$location'
,
'$routeParams'
,
'$scope'
,
'annotator'
,
'streamer'
,
'streamfilter'
'annotator'
,
'streamer'
,
'st
ore'
,
'st
reamfilter'
]
constructor
:
(
$location
,
$routeParams
,
$scope
,
annotator
,
streamer
,
streamfilter
annotator
,
streamer
,
st
ore
,
st
reamfilter
)
->
# Tells the view that these annotations are standalone
$scope
.
isEmbedded
=
false
$scope
.
isStream
=
false
# Clear out loaded annotations and threads
# XXX: Resolve threading, storage, and streamer better for all routes.
annotator
.
plugins
.
Threading
?
.
pluginInit
()
annotator
.
plugins
.
Store
?
.
annotations
=
[]
# Provide no-ops until these methods are moved elsewere. They only apply
# to annotations loaded into the stream.
$scope
.
focus
=
angular
.
noop
...
...
@@ -210,11 +138,10 @@ class AnnotationViewerController
$location
.
path
(
'/stream'
).
search
(
'q'
,
query
)
id
=
$routeParams
.
id
$scope
.
$watch
'store'
,
->
if
$scope
.
store
$scope
.
store
.
loadAnnotationsFromSearch
({
_id
:
id
}).
then
->
$scope
.
store
.
loadAnnotationsFromSearch
({
references
:
id
})
store
.
SearchResource
.
get
_id
:
$routeParams
.
id
,
({
rows
})
->
annotator
.
loadAnnotations
(
rows
)
store
.
SearchResource
.
get
references
:
$routeParams
.
id
,
({
rows
})
->
annotator
.
loadAnnotations
(
rows
)
streamfilter
.
setPastDataNone
()
...
...
@@ -225,12 +152,44 @@ class AnnotationViewerController
streamer
.
send
({
filter
:
streamfilter
.
getFilter
()})
class
ViewerController
this
.
$inject
=
[
'$scope'
,
'annotator'
]
constructor
:
(
$scope
,
annotator
)
->
this
.
$inject
=
[
'$scope'
,
'$route'
,
'annotator'
,
'auth'
,
'flash'
,
'streamer'
,
'streamfilter'
,
'store'
]
constructor
:
(
$scope
,
$route
,
annotator
,
auth
,
flash
,
streamer
,
streamfilter
,
store
)
->
# Tells the view that these annotations are embedded into the owner doc
$scope
.
isEmbedded
=
true
$scope
.
isStream
=
true
loaded
=
[]
loadAnnotations
=
->
if
annotator
.
tool
is
'highlight'
return
unless
auth
.
user
query
=
user
:
auth
.
user
for
p
in
annotator
.
providers
for
e
in
p
.
entities
when
e
not
in
loaded
loaded
.
push
e
store
.
SearchResource
.
get
angular
.
extend
(
uri
:
e
,
query
),
(
results
)
->
annotator
.
loadAnnotations
(
results
.
rows
)
streamfilter
.
resetFilter
().
addClause
(
'/uri'
,
'one_of'
,
loaded
)
if
auth
.
user
and
annotator
.
tool
is
'highlight'
streamfilter
.
addClause
(
'/user'
,
auth
.
user
)
streamer
.
send
({
filter
:
streamfilter
.
getFilter
()})
$scope
.
$watch
(
->
annotator
.
tool
),
(
newVal
,
oldVal
)
->
return
if
newVal
is
oldVal
$route
.
reload
()
$scope
.
$watchCollection
(
->
annotator
.
providers
),
loadAnnotations
$scope
.
focus
=
(
annotation
)
->
if
angular
.
isObject
annotation
highlights
=
[
annotation
.
$
$tag
]
...
...
h/static/scripts/directives/annotation.coffee
View file @
5279e095
...
...
@@ -159,9 +159,11 @@ AnnotationController = [
switch @action
when 'create'
annotator.publish 'annotationCreated', model
model.$create().then ->
annotator.publish 'annotationCreated', model
when 'delete', 'edit'
annotator.publish 'annotationUpdated', model
model.$update(id: model.id).then ->
annotator.publish 'annotationUpdated', model
@editing = false
@action = 'view'
...
...
@@ -181,8 +183,7 @@ AnnotationController = [
# Construct the reply.
references = [references..., id]
reply = {references, uri}
annotator.publish 'beforeAnnotationCreated', reply
reply = annotator.createAnnotation {references, uri}
if auth.user?
if permissions.isPublic model.permissions
...
...
@@ -275,9 +276,11 @@ AnnotationController = [
# Save highlights once logged in.
if highlight and this.isHighlight()
if auth.user
if model.user and not model.id
highlight = false # skip this on future updates
model.permissions = permissions.private()
annotator.publish 'annotationCreated', model
model.$create().then ->
annotator.publish 'annotationCreated', model
highlight = false # skip this on future updates
else
drafts.add model, => this.revert()
...
...
@@ -309,17 +312,6 @@ annotation = [
'$document'
,
'annotator'
,
(
$document
,
annotator
)
->
linkFn
=
(
scope
,
elem
,
attrs
,
[
ctrl
,
thread
,
threadFilter
,
counter
])
->
# Helper function to remove the temporary thread created for a new reply.
prune
=
(
message
)
->
return
if
message
.
id
?
# threading plugin will take care of it
return
unless
thread
.
container
.
message
is
message
thread
.
container
.
parent
?
.
removeChild
(
thread
.
container
)
if
thread
?
annotator
.
subscribe
'annotationDeleted'
,
prune
scope
.
$on
'$destroy'
,
->
annotator
.
unsubscribe
'annotationDeleted'
,
prune
# Observe the embedded attribute
attrs
.
$observe
'annotationEmbedded'
,
(
value
)
->
ctrl
.
embedded
=
value
?
and
value
!=
'false'
...
...
h/static/scripts/permissions-service.coffee
View file @
5279e095
###*
# @ngdoc service
# @name Permissions
...
...
h/static/scripts/plugin/discovery.coffee
deleted
100644 → 0
View file @
5964631d
class
Annotator
.
Plugin
.
Discovery
extends
Annotator
.
Plugin
pluginInit
:
->
svc
=
$
(
'link'
)
.
filter
->
this
.
rel
is
'service'
and
this
.
type
is
'application/annotatorsvc+json'
.
filter
->
this
.
href
return
unless
svc
.
length
href
=
svc
[
0
].
href
$
.
getJSON
href
,
(
data
)
=>
return
unless
data
?
.
links
?
options
=
prefix
:
href
.
replace
/\/$/
,
''
urls
:
{}
if
data
.
links
.
search
?
.
url
?
options
.
urls
.
search
=
data
.
links
.
search
.
url
for
action
,
info
of
(
data
.
links
.
annotation
or
{})
when
info
.
url
?
options
.
urls
[
action
]
=
info
.
url
for
action
,
url
of
options
.
urls
options
.
urls
[
action
]
=
url
.
replace
(
options
.
prefix
,
''
)
@
annotator
.
publish
'serviceDiscovery'
,
options
h/static/scripts/plugin/threading.coffee
View file @
5279e095
...
...
@@ -6,6 +6,7 @@ class Annotator.Plugin.Threading extends Annotator.Plugin
events
:
'beforeAnnotationCreated'
:
'beforeAnnotationCreated'
'annotationCreated'
:
'annotationCreated'
'annotationDeleted'
:
'annotationDeleted'
'annotationsLoaded'
:
'annotationsLoaded'
...
...
@@ -59,14 +60,32 @@ class Annotator.Plugin.Threading extends Annotator.Plugin
if
!
container
.
message
&&
container
.
children
.
length
==
0
parent
.
removeChild
(
container
)
delete
this
.
idTable
[
container
.
message
?
.
id
]
beforeAnnotationCreated
:
(
annotation
)
=>
this
.
thread
([
annotation
])
annotationDeleted
:
({
id
})
=>
container
=
this
.
getContainer
id
container
.
message
=
null
this
.
pruneEmpties
(
@
root
)
annotationCreated
:
(
annotation
)
=>
references
=
annotation
.
references
or
[]
if
typeof
(
annotation
.
references
)
==
'string'
then
references
=
[]
ref
=
references
[
references
.
length
-
1
]
parent
=
if
ref
then
@
idTable
[
ref
]
else
@
root
for
child
in
(
parent
.
children
or
[])
when
child
.
message
is
annotation
@
idTable
[
annotation
.
id
]
=
child
break
annotationDeleted
:
(
annotation
)
=>
if
id
of
this
.
idTable
container
=
this
.
idTable
[
id
]
container
.
message
=
null
delete
this
.
idTable
[
id
]
this
.
pruneEmpties
(
@
root
)
else
for
id
,
container
of
this
.
idTable
for
child
in
container
.
children
when
child
.
message
is
annotation
child
.
message
=
null
this
.
pruneEmpties
(
@
root
)
return
annotationsLoaded
:
(
annotations
)
=>
messages
=
(
@
root
.
flattenChildren
()
or
[]).
concat
(
annotations
)
...
...
h/static/scripts/services.coffee
View file @
5279e095
...
...
@@ -29,15 +29,12 @@ renderFactory = ['$$rAF', ($$rAF) ->
class
Hypothesis
extends
Annotator
events
:
'beforeAnnotationCreated'
:
'beforeAnnotationCreated'
'annotationCreated'
:
'digest'
'annotationDeleted'
:
'annotationDeleted'
'annotationUpdated'
:
'digest'
'annotationsLoaded'
:
'digest'
# Plugin configuration
options
:
noDocAccess
:
true
Discovery
:
{}
Threading
:
{}
# Internal state
...
...
@@ -47,17 +44,12 @@ class Hypothesis extends Annotator
tool
:
'comment'
visibleHighlights
:
false
this
.
$inject
=
[
'$document'
,
'$window'
]
constructor
:
(
$document
,
$window
)
->
this
.
$inject
=
[
'$document'
,
'$window'
,
'store'
]
constructor
:
(
$document
,
$window
,
store
)
->
super
(
$document
.
find
'body'
)
window
.
annotator
=
this
@
providers
=
[]
@
socialView
=
name
:
"none"
# "single-player"
this
.
patch_store
()
@
store
=
store
# Load plugins
for
own
name
,
opts
of
@
options
...
...
@@ -69,13 +61,13 @@ class Hypothesis extends Annotator
whitelist
=
[
'target'
,
'document'
,
'uri'
]
this
.
addPlugin
'Bridge'
,
gateway
:
true
formatter
:
(
annotation
)
=
>
formatter
:
(
annotation
)
-
>
formatted
=
{}
for
k
,
v
of
annotation
when
k
in
whitelist
formatted
[
k
]
=
v
formatted
parser
:
(
annotation
)
=
>
parsed
=
{}
parser
:
(
annotation
)
-
>
parsed
=
new
store
.
AnnotationResource
()
for
k
,
v
of
annotation
when
k
in
whitelist
parsed
[
k
]
=
v
parsed
...
...
@@ -84,21 +76,16 @@ class Hypothesis extends Annotator
window
:
source
origin
:
origin
scope
:
"
#{
scope
}
:provider"
onReady
:
=>
if
source
is
$window
.
parent
then
@
host
=
channel
entities
=
[]
onReady
:
=>
if
source
is
$window
.
parent
then
@
host
=
channel
channel
=
this
.
_setupXDM
options
provider
=
channel
:
channel
,
entities
:
[]
channel
.
call
method
:
'getDocumentInfo'
success
:
(
info
)
=>
entityUris
=
{}
entityUris
[
info
.
uri
]
=
true
for
link
in
info
.
metadata
.
link
entityUris
[
link
.
href
]
=
true
if
link
.
href
for
href
of
entityUris
entities
.
push
href
this
.
plugins
.
Store
?
.
loadAnnotations
()
provider
.
entities
=
(
link
.
href
for
link
in
info
.
metadata
.
link
)
@
providers
.
push
provider
@
element
.
scope
().
$digest
()
this
.
digest
()
# Allow the host to define it's own state
...
...
@@ -111,10 +98,6 @@ class Hypothesis extends Annotator
method
:
'setVisibleHighlights'
params
:
this
.
visibleHighlights
@
providers
.
push
channel
:
channel
entities
:
entities
_setupXDM
:
(
options
)
->
# jschannel chokes FF and Chrome extension origins.
if
(
options
.
origin
.
match
/^chrome-extension:\/\//
)
or
...
...
@@ -195,6 +178,27 @@ class Hypothesis extends Annotator
_setupDocumentAccessStrategies
:
->
this
_scan
:
->
this
createAnnotation
:
(
annotation
)
->
annotation
=
new
@
store
.
AnnotationResource
(
annotation
)
this
.
publish
'beforeAnnotationCreated'
,
annotation
annotation
deleteAnnotation
:
(
annotation
)
->
annotation
.
$delete
(
id
:
annotation
.
id
).
then
=>
this
.
publish
'annotationDeleted'
,
annotation
annotation
loadAnnotations
:
(
annotations
)
->
annotations
=
for
annotation
in
annotations
container
=
@
plugins
.
Threading
.
idTable
[
annotation
.
id
]
if
container
?
.
message
angular
.
copy
annotation
,
container
.
message
this
.
publish
'annotationUpdated'
,
container
.
message
continue
else
annotation
super
(
new
@
store
.
AnnotationResource
(
a
)
for
a
in
annotations
)
# Do nothing in the app frame, let the host handle it.
setupAnnotation
:
(
annotation
)
->
annotation
...
...
@@ -263,77 +267,9 @@ class Hypothesis extends Annotator
if
scope
.
selectedAnnotations
?
[
annotation
.
id
]
delete
scope
.
selectedAnnotations
[
annotation
.
id
]
@
_setSelectedAnnotations
scope
.
selectedAnnotations
@
digest
()
patch_store
:
->
scope
=
@
element
.
scope
()
Store
=
Annotator
.
Plugin
.
Store
# When the Store plugin is first instantiated, don't load annotations.
# They will be loaded manually as entities are registered by participating
# frames.
Store
.
prototype
.
loadAnnotations
=
->
query
=
limit
:
1000
@
annotator
.
considerSocialView
.
call
@
annotator
,
query
entities
=
{}
for
p
in
@
annotator
.
providers
for
uri
in
p
.
entities
unless
entities
[
uri
]
?
entities
[
uri
]
=
true
this
.
loadAnnotationsFromSearch
(
angular
.
extend
{},
query
,
uri
:
uri
)
this
.
entities
=
Object
.
keys
(
entities
)
# When the store plugin finishes a request, update the annotation
# using a monkey-patched update function which updates the threading
# if the annotation has a newly-assigned id and ensures that the id
# is enumerable.
Store
.
prototype
.
updateAnnotation
=
(
annotation
,
data
)
=>
# Update the annotation with the new data
annotation
=
angular
.
extend
annotation
,
data
# Update the thread table
update
=
(
parent
)
->
for
child
in
parent
.
children
when
child
.
message
is
annotation
scope
.
threading
.
idTable
[
data
.
id
]
=
child
return
true
return
false
# Check its references
references
=
annotation
.
references
or
[]
if
typeof
(
annotation
.
references
)
==
'string'
then
references
=
[]
for
ref
in
references
.
slice
().
reverse
()
container
=
scope
.
threading
.
idTable
[
ref
]
continue
unless
container
?
break
if
update
container
# Check the root
update
scope
.
threading
.
root
# Update the view
this
.
digest
()
considerSocialView
:
(
query
)
->
switch
@
socialView
.
name
when
"none"
# Sweet, nothing to do, just clean up previous filters
delete
query
.
user
when
"single-player"
if
@
user
?
query
.
user
=
@
element
.
injector
().
get
(
'auth'
).
user
else
delete
query
.
user
setTool
:
(
name
)
->
return
if
name
is
@
tool
if
name
is
'highlight'
this
.
socialView
.
name
=
'single-player'
else
this
.
socialView
.
name
=
'none'
@
tool
=
name
this
.
publish
'setTool'
,
name
for
p
in
@
providers
...
...
h/static/scripts/store-service.coffee
0 → 100644
View file @
5279e095
###*
# @ngdoc service
# @name store
#
# @description
# The `store` service handles the backend calls for the restful API. This is
# created dynamically from the API index as the angular $resource() method
# supports the same keys as the index document. This will make a resource
# constructor for each endpoint eg. store.AnnotationResource() and
# store.SearchResource().
###
angular
.
module
(
'h'
)
.
service
(
'store'
,
[
'$document'
,
'$http'
,
'$resource'
,
(
$document
,
$http
,
$resource
)
->
# Find any service link tag
svc
=
$document
.
find
(
'link'
)
.
filter
->
@
rel
is
'service'
and
@
type
is
'application/annotatorsvc+json'
.
filter
->
@
href
.
prop
(
'href'
)
camelize
=
(
string
)
->
string
.
replace
/(?:^|_)([a-z])/g
,
(
_
,
char
)
->
char
.
toUpperCase
()
store
=
$resolved
:
false
# We call the service_url and the backend api gives back
# the actions and urls it provides.
$promise
:
$http
.
get
(
svc
)
.
finally
->
store
.
$resolved
=
true
.
then
(
response
)
->
for
name
,
actions
of
response
.
data
.
links
# For each action name we configure an ng-resource.
# For the search resource, one URL is given for all actions.
# For the annotations, each action has its own URL.
prop
=
"
#{
camelize
(
name
)
}
Resource"
store
[
prop
]
=
$resource
(
actions
.
url
or
svc
,
{},
actions
)
store
])
h/static/scripts/streamer-service.coffee
View file @
5279e095
...
...
@@ -5,7 +5,7 @@ ST_CLOSED = 3
###*
# @ngdoc service
# @name
S
treamer
# @name
s
treamer
#
# @property {string} clientId A unique identifier for this client.
#
...
...
@@ -59,7 +59,6 @@ class Streamer
# Give the application a chance to initialize the connection
self.onopen(name: 'open')
# Process queued messages
self._sendQueue()
...
...
h/static/scripts/streamsearch.coffee
View file @
5279e095
class
StreamSearchController
this
.
inject
=
[
'$scope'
,
'$rootScope'
,
'$routeParams'
,
'annotator'
,
'queryparser'
,
'searchfilter'
,
'streamer'
,
'streamfilter'
'annotator'
,
'auth'
,
'queryparser'
,
'searchfilter'
,
'store'
,
'streamer'
,
'streamfilter'
]
constructor
:
(
$scope
,
$rootScope
,
$routeParams
annotator
,
queryparser
,
searchfilter
,
streamer
,
streamfilter
annotator
,
auth
,
queryparser
,
searchfilter
,
store
,
streamer
,
streamfilter
)
->
# Clear out loaded annotations and threads
# XXX: Resolve threading, storage, and streamer better for all routes.
annotator
.
plugins
.
Threading
?
.
pluginInit
()
annotator
.
plugins
.
Store
?
.
annotations
=
[]
# Initialize the base filter
streamfilter
.
resetFilter
()
.
setMatchPolicyIncludeAll
()
.
setPastDataHits
(
50
)
# Apply query clauses
$scope
.
search
.
query
=
$routeParams
.
q
...
...
@@ -30,10 +26,14 @@ class StreamSearchController
$scope
.
shouldShowThread
=
(
container
)
->
true
streamer
.
send
({
filter
:
streamfilter
.
getFilter
()})
$scope
.
$on
'$destroy'
,
->
$scope
.
search
.
query
=
''
$scope
.
$watch
(
->
auth
.
user
),
->
query
=
angular
.
extend
limit
:
10
,
$scope
.
search
.
query
store
.
SearchResource
.
get
query
,
({
rows
})
->
annotator
.
loadAnnotations
(
rows
)
angular
.
module
(
'h'
)
.
controller
(
'StreamSearchController'
,
StreamSearchController
)
h/static/scripts/vendor/annotator.store.js
deleted
100644 → 0
View file @
5964631d
// Generated by CoffeeScript 1.6.3
/*
** Annotator 1.2.6-dev-6623b2c
** https://github.com/okfn/annotator/
**
** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning.
** Dual licensed under the MIT and GPLv3 licenses.
** https://github.com/okfn/annotator/blob/master/LICENSE
**
** Built at: 2014-12-11 01:33:34Z
*/
/*
//
*/
// Generated by CoffeeScript 1.6.3
(
function
()
{
var
__bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
},
__hasProp
=
{}.
hasOwnProperty
,
__extends
=
function
(
child
,
parent
)
{
for
(
var
key
in
parent
)
{
if
(
__hasProp
.
call
(
parent
,
key
))
child
[
key
]
=
parent
[
key
];
}
function
ctor
()
{
this
.
constructor
=
child
;
}
ctor
.
prototype
=
parent
.
prototype
;
child
.
prototype
=
new
ctor
();
child
.
__super__
=
parent
.
prototype
;
return
child
;
},
__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
;
};
Annotator
.
Plugin
.
Store
=
(
function
(
_super
)
{
__extends
(
Store
,
_super
);
Store
.
prototype
.
events
=
{
'annotationCreated'
:
'annotationCreated'
,
'annotationDeleted'
:
'annotationDeleted'
,
'annotationUpdated'
:
'annotationUpdated'
};
Store
.
prototype
.
options
=
{
annotationData
:
{},
emulateHTTP
:
false
,
loadFromSearch
:
false
,
prefix
:
'/store'
,
urls
:
{
create
:
'/annotations'
,
read
:
'/annotations/:id'
,
update
:
'/annotations/:id'
,
destroy
:
'/annotations/:id'
,
search
:
'/search'
}
};
function
Store
(
element
,
options
)
{
this
.
_onError
=
__bind
(
this
.
_onError
,
this
);
this
.
_onLoadAnnotationsFromSearch
=
__bind
(
this
.
_onLoadAnnotationsFromSearch
,
this
);
this
.
_onLoadAnnotations
=
__bind
(
this
.
_onLoadAnnotations
,
this
);
this
.
_getAnnotations
=
__bind
(
this
.
_getAnnotations
,
this
);
Store
.
__super__
.
constructor
.
apply
(
this
,
arguments
);
this
.
annotations
=
[];
}
Store
.
prototype
.
pluginInit
=
function
()
{
if
(
!
Annotator
.
supported
())
{
return
;
}
if
(
this
.
annotator
.
plugins
.
Auth
)
{
return
this
.
annotator
.
plugins
.
Auth
.
withToken
(
this
.
_getAnnotations
);
}
else
{
return
this
.
_getAnnotations
();
}
};
Store
.
prototype
.
_getAnnotations
=
function
()
{
if
(
this
.
options
.
loadFromSearch
)
{
return
this
.
loadAnnotationsFromSearch
(
this
.
options
.
loadFromSearch
);
}
else
{
return
this
.
loadAnnotations
();
}
};
Store
.
prototype
.
annotationCreated
=
function
(
annotation
)
{
var
_this
=
this
;
if
(
__indexOf
.
call
(
this
.
annotations
,
annotation
)
<
0
)
{
this
.
registerAnnotation
(
annotation
);
return
this
.
_apiRequest
(
'create'
,
annotation
,
function
(
data
)
{
if
(
data
.
id
==
null
)
{
console
.
warn
(
Annotator
.
_t
(
"Warning: No ID returned from server for annotation "
),
annotation
);
}
return
_this
.
updateAnnotation
(
annotation
,
data
);
});
}
else
{
return
this
.
updateAnnotation
(
annotation
,
{});
}
};
Store
.
prototype
.
annotationUpdated
=
function
(
annotation
)
{
var
_this
=
this
;
if
(
__indexOf
.
call
(
this
.
annotations
,
annotation
)
>=
0
)
{
return
this
.
_apiRequest
(
'update'
,
annotation
,
(
function
(
data
)
{
return
_this
.
updateAnnotation
(
annotation
,
data
);
}));
}
};
Store
.
prototype
.
annotationDeleted
=
function
(
annotation
)
{
var
_this
=
this
;
if
(
__indexOf
.
call
(
this
.
annotations
,
annotation
)
>=
0
)
{
return
this
.
_apiRequest
(
'destroy'
,
annotation
,
(
function
()
{
return
_this
.
unregisterAnnotation
(
annotation
);
}));
}
};
Store
.
prototype
.
registerAnnotation
=
function
(
annotation
)
{
return
this
.
annotations
.
push
(
annotation
);
};
Store
.
prototype
.
unregisterAnnotation
=
function
(
annotation
)
{
return
this
.
annotations
.
splice
(
this
.
annotations
.
indexOf
(
annotation
),
1
);
};
Store
.
prototype
.
updateAnnotation
=
function
(
annotation
,
data
)
{
if
(
__indexOf
.
call
(
this
.
annotations
,
annotation
)
<
0
)
{
console
.
error
(
Annotator
.
_t
(
"Trying to update unregistered annotation!"
));
}
else
{
$
.
extend
(
annotation
,
data
);
}
return
$
(
annotation
.
highlights
).
data
(
'annotation'
,
annotation
);
};
Store
.
prototype
.
loadAnnotations
=
function
()
{
return
this
.
_apiRequest
(
'read'
,
null
,
this
.
_onLoadAnnotations
);
};
Store
.
prototype
.
_onLoadAnnotations
=
function
(
data
)
{
var
a
,
annotation
,
annotationMap
,
newData
,
_i
,
_j
,
_len
,
_len1
,
_ref
;
if
(
data
==
null
)
{
data
=
[];
}
annotationMap
=
{};
_ref
=
this
.
annotations
;
for
(
_i
=
0
,
_len
=
_ref
.
length
;
_i
<
_len
;
_i
++
)
{
a
=
_ref
[
_i
];
annotationMap
[
a
.
id
]
=
a
;
}
newData
=
[];
for
(
_j
=
0
,
_len1
=
data
.
length
;
_j
<
_len1
;
_j
++
)
{
a
=
data
[
_j
];
if
(
annotationMap
[
a
.
id
])
{
annotation
=
annotationMap
[
a
.
id
];
this
.
updateAnnotation
(
annotation
,
a
);
}
else
{
newData
.
push
(
a
);
}
}
this
.
annotations
=
this
.
annotations
.
concat
(
newData
);
return
this
.
annotator
.
loadAnnotations
(
newData
.
slice
());
};
Store
.
prototype
.
loadAnnotationsFromSearch
=
function
(
searchOptions
)
{
return
this
.
_apiRequest
(
'search'
,
searchOptions
,
this
.
_onLoadAnnotationsFromSearch
);
};
Store
.
prototype
.
_onLoadAnnotationsFromSearch
=
function
(
data
)
{
if
(
data
==
null
)
{
data
=
{};
}
return
this
.
_onLoadAnnotations
(
data
.
rows
||
[]);
};
Store
.
prototype
.
dumpAnnotations
=
function
()
{
var
ann
,
_i
,
_len
,
_ref
,
_results
;
_ref
=
this
.
annotations
;
_results
=
[];
for
(
_i
=
0
,
_len
=
_ref
.
length
;
_i
<
_len
;
_i
++
)
{
ann
=
_ref
[
_i
];
_results
.
push
(
JSON
.
parse
(
this
.
_dataFor
(
ann
)));
}
return
_results
;
};
Store
.
prototype
.
_apiRequest
=
function
(
action
,
obj
,
onSuccess
)
{
var
id
,
options
,
request
,
url
;
id
=
obj
&&
obj
.
id
;
url
=
this
.
_urlFor
(
action
,
id
);
options
=
this
.
_apiRequestOptions
(
action
,
obj
,
onSuccess
);
request
=
$
.
ajax
(
url
,
options
);
request
.
_id
=
id
;
request
.
_action
=
action
;
return
request
;
};
Store
.
prototype
.
_apiRequestOptions
=
function
(
action
,
obj
,
onSuccess
)
{
var
data
,
method
,
opts
;
method
=
this
.
_methodFor
(
action
);
opts
=
{
type
:
method
,
headers
:
this
.
element
.
data
(
'annotator:headers'
),
dataType
:
"json"
,
success
:
onSuccess
||
function
()
{},
error
:
this
.
_onError
};
if
(
this
.
options
.
emulateHTTP
&&
(
method
===
'PUT'
||
method
===
'DELETE'
))
{
opts
.
headers
=
$
.
extend
(
opts
.
headers
,
{
'X-HTTP-Method-Override'
:
method
});
opts
.
type
=
'POST'
;
}
if
(
action
===
"search"
)
{
opts
=
$
.
extend
(
opts
,
{
data
:
obj
});
return
opts
;
}
data
=
obj
&&
this
.
_dataFor
(
obj
);
if
(
this
.
options
.
emulateJSON
)
{
opts
.
data
=
{
json
:
data
};
if
(
this
.
options
.
emulateHTTP
)
{
opts
.
data
.
_method
=
method
;
}
return
opts
;
}
opts
=
$
.
extend
(
opts
,
{
data
:
data
,
contentType
:
"application/json; charset=utf-8"
});
return
opts
;
};
Store
.
prototype
.
_urlFor
=
function
(
action
,
id
)
{
var
url
;
url
=
this
.
options
.
prefix
!=
null
?
this
.
options
.
prefix
:
''
;
url
+=
this
.
options
.
urls
[
action
];
url
=
url
.
replace
(
/
\/
:id/
,
id
!=
null
?
'/'
+
id
:
''
);
url
=
url
.
replace
(
/:id/
,
id
!=
null
?
id
:
''
);
return
url
;
};
Store
.
prototype
.
_methodFor
=
function
(
action
)
{
var
table
;
table
=
{
'create'
:
'POST'
,
'read'
:
'GET'
,
'update'
:
'PUT'
,
'destroy'
:
'DELETE'
,
'search'
:
'GET'
};
return
table
[
action
];
};
Store
.
prototype
.
_dataFor
=
function
(
annotation
)
{
var
data
,
field
,
k
,
saved
,
v
,
_i
,
_len
,
_ref
;
saved
=
{};
_ref
=
[
"highlights"
,
"anchors"
];
for
(
_i
=
0
,
_len
=
_ref
.
length
;
_i
<
_len
;
_i
++
)
{
field
=
_ref
[
_i
];
if
(
annotation
[
field
]
!=
null
)
{
saved
[
field
]
=
annotation
[
field
];
delete
annotation
[
field
];
}
}
$
.
extend
(
annotation
,
this
.
options
.
annotationData
);
data
=
JSON
.
stringify
(
annotation
);
for
(
k
in
saved
)
{
v
=
saved
[
k
];
annotation
[
k
]
=
v
;
}
return
data
;
};
Store
.
prototype
.
_onError
=
function
(
xhr
)
{
var
action
,
message
;
action
=
xhr
.
_action
;
message
=
Annotator
.
_t
(
"Sorry we could not "
)
+
action
+
Annotator
.
_t
(
" this annotation"
);
if
(
xhr
.
_action
===
'search'
)
{
message
=
Annotator
.
_t
(
"Sorry we could not search the store for annotations"
);
}
else
if
(
xhr
.
_action
===
'read'
&&
!
xhr
.
_id
)
{
message
=
Annotator
.
_t
(
"Sorry we could not "
)
+
action
+
Annotator
.
_t
(
" the annotations from the store"
);
}
switch
(
xhr
.
status
)
{
case
401
:
message
=
Annotator
.
_t
(
"Sorry you are not allowed to "
)
+
action
+
Annotator
.
_t
(
" this annotation"
);
break
;
case
404
:
message
=
Annotator
.
_t
(
"Sorry we could not connect to the annotations store"
);
break
;
case
500
:
message
=
Annotator
.
_t
(
"Sorry something went wrong with the annotation store"
);
}
Annotator
.
showNotification
(
message
,
Annotator
.
Notification
.
ERROR
);
return
console
.
error
(
Annotator
.
_t
(
"API request failed:"
)
+
(
" '"
+
xhr
.
status
+
"'"
));
};
return
Store
;
})(
Annotator
.
Plugin
);
}).
call
(
this
);
karma.config.js
View file @
5279e095
...
...
@@ -37,7 +37,6 @@ module.exports = function(config) {
'h/static/scripts/vendor/annotator.js'
,
'h/static/scripts/vendor/annotator.auth.js'
,
'h/static/scripts/vendor/annotator.document.js'
,
'h/static/scripts/vendor/annotator.store.js'
,
'h/static/scripts/plugin/bridge.js'
,
'h/static/scripts/plugin/discovery.js'
,
'h/static/scripts/plugin/threading.js'
,
...
...
tests/js/auth-service-test.coffee
View file @
5279e095
...
...
@@ -43,9 +43,11 @@ describe 'h', ->
describe
'auth service'
,
->
$http
=
null
auth
=
null
beforeEach
inject
(
_auth_
)
->
beforeEach
inject
(
_$http_
,
_auth_
)
->
$http
=
_$http_
auth
=
_auth_
it
'watches the identity service for identity change events'
,
->
...
...
@@ -56,20 +58,36 @@ describe 'h', ->
onready
()
assert
.
isNull
(
auth
.
user
)
it
'sets auth.user
at login'
,
->
{
onlogin
}
=
fakeIdentity
.
watch
.
args
[
0
][
0
]
onlogin
(
'test-assertion'
)
fakeToken
=
{
userId
:
'acct:hey@joe'
}
userSetter
=
fakeAnnotator
.
plugins
.
Auth
.
withToken
.
args
[
0
][
0
]
userSetter
(
fakeToken
)
assert
.
equal
(
auth
.
user
,
'acct:hey@joe'
)
describe
'
at login'
,
->
beforeEach
->
{
onlogin
}
=
fakeIdentity
.
watch
.
args
[
0
][
0
]
onlogin
(
'test-assertion'
)
fakeToken
=
{
userId
:
'acct:hey@joe'
}
userSetter
=
fakeAnnotator
.
plugins
.
Auth
.
withToken
.
args
[
0
][
0
]
userSetter
(
fakeToken
)
it
'destroys the plugin at logout and sets auth.user to null'
,
->
{
onlogout
}
=
fakeIdentity
.
watch
.
args
[
0
][
0
]
auth
.
user
=
'acct:hey@joe'
authPlugin
=
fakeAnnotator
.
plugins
.
Auth
onlogout
()
it
'sets auth.user'
,
->
assert
.
equal
(
auth
.
user
,
'acct:hey@joe'
)
assert
.
called
(
authPlugin
.
destroy
)
assert
.
equal
(
auth
.
user
,
null
)
it
'sets the token header as a default header'
,
->
token
=
$http
.
defaults
.
headers
.
common
[
'X-Annotator-Auth-Token'
]
assert
.
equal
(
token
,
'test-assertion'
)
describe
'at logout'
,
->
authPlugin
=
null
beforeEach
->
{
onlogout
}
=
fakeIdentity
.
watch
.
args
[
0
][
0
]
auth
.
user
=
'acct:hey@joe'
authPlugin
=
fakeAnnotator
.
plugins
.
Auth
onlogout
()
it
'destroys the plugin'
,
->
assert
.
called
(
authPlugin
.
destroy
)
it
'sets auth.user to null'
,
->
assert
.
equal
(
auth
.
user
,
null
)
it
'unsets the token header'
,
->
token
=
$http
.
defaults
.
headers
.
common
[
'X-Annotator-Auth-Token'
]
assert
.
isUndefined
(
token
)
tests/js/controllers-test.coffee
View file @
5279e095
assert
=
chai
.
assert
sinon
.
assert
.
expose
assert
,
prefix
:
null
fakeStore
=
SearchResource
:
get
:
sinon
.
spy
()
describe
'h'
,
->
$scope
=
null
fakeAuth
=
null
...
...
@@ -75,6 +80,7 @@ describe 'h', ->
$scope
.
search
=
{}
annotationViewer
=
$controller
'AnnotationViewerController'
,
$scope
:
$scope
store
:
fakeStore
it
'sets the isEmbedded property to false'
,
->
assert
.
isFalse
(
$scope
.
isEmbedded
)
tests/js/directives/annotation-test.coffee
View file @
5279e095
...
...
@@ -11,6 +11,7 @@ describe 'h.directives.annotation', ->
createController
=
null
flash
=
null
fakeAuth
=
null
fakeStore
=
null
fakeUser
=
null
beforeEach
module
(
'h'
)
...
...
@@ -21,6 +22,7 @@ describe 'h.directives.annotation', ->
user
:
'acct:bill@localhost'
$provide
.
value
'auth'
,
fakeAuth
$provide
.
value
'store'
,
fakeStore
return
beforeEach
inject
(
_$compile_
,
$controller
,
_$document_
,
$rootScope
,
_$timeout_
)
->
...
...
@@ -29,7 +31,11 @@ describe 'h.directives.annotation', ->
$timeout
=
_$timeout_
$scope
=
$rootScope
.
$new
()
$scope
.
annotationGet
=
(
locals
)
->
annotation
annotator
=
{
plugins
:
{},
publish
:
sandbox
.
spy
()}
annotator
=
{
createAnnotation
:
sandbox
.
spy
(
data
)
->
data
plugins
:
{},
publish
:
sandbox
.
spy
()
}
annotation
=
id
:
'deadbeef'
document
:
...
...
@@ -48,6 +54,30 @@ describe 'h.directives.annotation', ->
afterEach
->
sandbox
.
restore
()
describe
'when the annotation is a highlight'
,
->
beforeEach
->
annotator
.
tool
=
'highlight'
annotation
.
$create
=
sinon
.
stub
().
returns
then
:
angular
.
noop
catch
:
angular
.
noop
finally
:
angular
.
noop
it
'persists upon login'
,
->
delete
annotation
.
id
delete
annotation
.
user
controller
=
createController
()
$scope
.
$digest
()
assert
.
notCalled
annotation
.
$create
annotation
.
user
=
'acct:ted@wyldstallyns.com'
$scope
.
$digest
()
assert
.
calledOnce
annotation
.
$create
it
'is private'
,
->
delete
annotation
.
id
controller
=
createController
()
$scope
.
$digest
()
assert
controller
.
isPrivate
()
describe
'#reply'
,
->
controller
=
null
container
=
null
...
...
@@ -72,22 +102,22 @@ describe 'h.directives.annotation', ->
it
'creates a new reply with the proper uri and references'
,
->
controller
.
reply
()
match
=
sinon
.
match
{
references
:
[
annotation
.
id
],
uri
:
annotation
.
uri
}
assert
.
calledWith
(
annotator
.
publish
,
'beforeAnnotationCreated'
,
match
)
assert
.
calledWith
(
annotator
.
createAnnotation
,
match
)
it
'adds the world readable principal if the parent is public'
,
->
annotation
.
permissions
.
read
.
push
(
'group:__world__'
)
controller
.
reply
()
newAnnotation
=
annotator
.
publish
.
lastCall
.
args
[
1
]
newAnnotation
=
annotator
.
createAnnotation
.
lastCall
.
args
[
0
]
assert
.
include
(
newAnnotation
.
permissions
.
read
,
'group:__world__'
)
it
'does not add the world readable principal if the parent is private'
,
->
controller
.
reply
()
newAnnotation
=
annotator
.
publish
.
lastCall
.
args
[
1
]
newAnnotation
=
annotator
.
createAnnotation
.
lastCall
.
args
[
0
]
assert
.
notInclude
(
newAnnotation
.
permissions
.
read
,
'group:__world__'
)
it
'fills the other permissions too'
,
->
controller
.
reply
()
newAnnotation
=
annotator
.
publish
.
lastCall
.
args
[
1
]
newAnnotation
=
annotator
.
createAnnotation
.
lastCall
.
args
[
0
]
assert
.
equal
(
newAnnotation
.
permissions
.
update
[
0
],
'acct:bill@localhost'
)
assert
.
equal
(
newAnnotation
.
permissions
.
delete
[
0
],
'acct:bill@localhost'
)
assert
.
equal
(
newAnnotation
.
permissions
.
admin
[
0
],
'acct:bill@localhost'
)
...
...
tests/js/directives/privacy-test.coffee
View file @
5279e095
...
...
@@ -17,7 +17,6 @@ describe 'h.directives.privacy', ->
describe
'memory fallback'
,
->
fakeAuth
=
null
fakeWindow
=
null
sandbox
=
null
beforeEach
module
(
$provide
)
->
...
...
@@ -27,24 +26,23 @@ describe 'h.directives.privacy', ->
user
:
'acct:angry.joe@texas.com'
}
fakeWindow
=
{
localStorage
:
undefined
}
$provide
.
value
'auth'
,
fakeAuth
$provide
.
value
'$window'
,
fakeWindow
return
afterEach
->
sandbox
.
restore
()
describe
'has memory fallback'
,
->
$window
=
null
$scope2
=
null
beforeEach
inject
(
_$compile_
,
_$rootScope_
)
->
beforeEach
inject
(
_$compile_
,
_$rootScope_
,
_$window_
)
->
$compile
=
_$compile_
$scope
=
_$rootScope_
.
$new
()
$scope2
=
_$rootScope_
.
$new
()
$window
=
_$window_
$window
.
localStorage
=
null
it
'stores the default visibility level when it changes'
,
->
$scope
.
permissions
=
{
read
:
[
'acct:user@example.com'
]}
...
...
tests/js/store-service-test.coffee
0 → 100644
View file @
5279e095
assert
=
chai
.
assert
sinon
.
assert
.
expose
assert
,
prefix
:
null
describe
'store'
,
->
$httpBackend
=
null
sandbox
=
null
store
=
null
fakeDocument
=
null
beforeEach
module
(
'h'
)
beforeEach
module
(
$provide
)
->
sandbox
=
sinon
.
sandbox
.
create
()
link
=
document
.
createElement
(
"link"
)
link
.
rel
=
'service'
link
.
type
=
'application/annotatorsvc+json'
link
.
href
=
'http://example.com/api'
fakeDocument
=
{
find
:
sandbox
.
stub
().
returns
(
$
(
link
))
}
$provide
.
value
'$document'
,
fakeDocument
return
afterEach
->
sandbox
.
restore
()
beforeEach
inject
(
$q
,
_$httpBackend_
,
_store_
)
->
$httpBackend
=
_$httpBackend_
store
=
_store_
$httpBackend
.
expectGET
(
'http://example.com/api'
).
respond
links
:
annotation
:
create
:
{
method
:
'POST'
url
:
'http://example.com/api/annotations'
}
delete
:
{}
read
:
{}
update
:
{}
search
:
url
:
'http://0.0.0.0:5000/api/search'
beware_dragons
:
url
:
'http://0.0.0.0:5000/api/roar'
$httpBackend
.
flush
()
it
'reads the operations from the backend'
,
->
assert
.
isFunction
(
store
.
AnnotationResource
,
'expected store.AnnotationResource to be a function'
)
assert
.
isFunction
(
store
.
BewareDragonsResource
,
'expected store.BewareDragonsResource to be a function'
)
assert
.
isFunction
(
store
.
SearchResource
,
'expected store.SearchResource to be a function'
)
it
'saves a new annotation'
,
->
annotation
=
{
id
:
'test'
}
annotation
=
new
store
.
AnnotationResource
(
annotation
)
saved
=
{}
annotation
.
$create
().
then
->
assert
.
isNotNull
(
saved
.
id
)
$httpBackend
.
expectPOST
(
'http://example.com/api/annotations'
,
annotation
).
respond
->
saved
.
id
=
annotation
.
id
return
[
201
,
{},
{}]
$httpBackend
.
flush
()
tests/js/streamer-service-test.coffee
View file @
5279e095
...
...
@@ -97,6 +97,7 @@ describe 'streamer', ->
msg
=
fakeSock
.
send
.
getCall
(
0
).
args
[
0
]
data
=
JSON
.
parse
(
msg
)
assert
.
equal
(
data
.
messageType
,
'client_id'
)
assert
.
equal
(
typeof
data
.
value
,
'string'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment