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
397f269a
Commit
397f269a
authored
Sep 10, 2020
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert `CrossFrame` class to JS
Convert this class to JS and make some basic documentation improvements.
parent
2e0eee75
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
260 additions
and
206 deletions
+260
-206
index.js
src/annotator/index.js
+0
-1
cross-frame.coffee
src/annotator/plugin/cross-frame.coffee
+0
-78
cross-frame.js
src/annotator/plugin/cross-frame.js
+112
-0
cross-frame-test.coffee
src/annotator/plugin/test/cross-frame-test.coffee
+0
-127
cross-frame-test.js
src/annotator/plugin/test/cross-frame-test.js
+148
-0
No files found.
src/annotator/index.js
View file @
397f269a
...
@@ -29,7 +29,6 @@ import DocumentPlugin from './plugin/document';
...
@@ -29,7 +29,6 @@ import DocumentPlugin from './plugin/document';
import
Guest
from
'./guest'
;
import
Guest
from
'./guest'
;
// @ts-expect-error
// @ts-expect-error
import
BucketBarPlugin
from
'./plugin/bucket-bar'
;
import
BucketBarPlugin
from
'./plugin/bucket-bar'
;
// @ts-expect-error
import
CrossFramePlugin
from
'./plugin/cross-frame'
;
import
CrossFramePlugin
from
'./plugin/cross-frame'
;
// @ts-expect-error
// @ts-expect-error
import
PDFPlugin
from
'./plugin/pdf'
;
import
PDFPlugin
from
'./plugin/pdf'
;
...
...
src/annotator/plugin/cross-frame.coffee
deleted
100644 → 0
View file @
2e0eee75
Plugin
=
require
(
'../plugin'
)
{
default
:
AnnotationSync
}
=
require
(
'../annotation-sync'
)
{
default
:
Bridge
}
=
require
(
'../../shared/bridge'
)
{
default
:
Discovery
}
=
require
(
'../../shared/discovery'
)
FrameUtil
=
require
(
'../util/frame-util'
)
{
default
:
FrameObserver
}
=
require
(
'../frame-observer'
)
# Extracts individual keys from an object and returns a new one.
extract
=
extract
=
(
obj
,
keys
...)
->
ret
=
{}
ret
[
key
]
=
obj
[
key
]
for
key
in
keys
when
obj
.
hasOwnProperty
(
key
)
ret
# Class for establishing a messaging connection to the parent sidebar as well
# as keeping the annotation state in sync with the sidebar application, this
# frame acts as the bridge client, the sidebar is the server. This plugin
# can also be used to send messages through to the sidebar using the
# call method. This plugin also enables the discovery and management of
# not yet known frames in a multiple frame scenario.
module
.
exports
=
class
CrossFrame
extends
Plugin
constructor
:
(
elem
,
options
)
->
super
config
=
options
.
config
opts
=
extract
(
options
,
'server'
)
discovery
=
new
Discovery
(
window
,
opts
)
bridge
=
new
Bridge
()
opts
=
extract
(
options
,
'on'
,
'emit'
)
annotationSync
=
new
AnnotationSync
(
bridge
,
opts
)
frameObserver
=
new
FrameObserver
(
elem
)
frameIdentifiers
=
new
Map
()
this
.
pluginInit
=
->
onDiscoveryCallback
=
(
source
,
origin
,
token
)
->
bridge
.
createChannel
(
source
,
origin
,
token
)
discovery
.
startDiscovery
(
onDiscoveryCallback
)
frameObserver
.
observe
(
_injectToFrame
,
_iframeUnloaded
);
this
.
destroy
=
->
# super doesnt work here :(
Plugin
::
destroy
.
apply
(
this
,
arguments
)
bridge
.
destroy
()
discovery
.
stopDiscovery
()
frameObserver
.
disconnect
()
this
.
sync
=
(
annotations
,
cb
)
->
annotationSync
.
sync
(
annotations
,
cb
)
this
.
on
=
(
event
,
fn
)
->
bridge
.
on
(
event
,
fn
)
this
.
call
=
(
message
,
args
...)
->
bridge
.
call
(
message
,
args
...)
this
.
onConnect
=
(
fn
)
->
bridge
.
onConnect
(
fn
)
_injectToFrame
=
(
frame
)
->
if
!
FrameUtil
.
hasHypothesis
(
frame
)
# Take the embed script location from the config
# until an alternative solution comes around.
clientUrl
=
config
.
clientUrl
FrameUtil
.
isLoaded
frame
,
()
->
subFrameIdentifier
=
discovery
.
generateToken
()
frameIdentifiers
.
set
(
frame
,
subFrameIdentifier
)
injectedConfig
=
Object
.
assign
({},
config
,
{
subFrameIdentifier
})
FrameUtil
.
injectHypothesis
(
frame
,
clientUrl
,
injectedConfig
)
_iframeUnloaded
=
(
frame
)
->
bridge
.
call
(
'destroyFrame'
,
frameIdentifiers
.
get
(
frame
))
frameIdentifiers
.
delete
(
frame
)
src/annotator/plugin/cross-frame.js
0 → 100644
View file @
397f269a
// @ts-expect-error - `Plugin` base class is still written in CoffeeScript
import
Plugin
from
'../plugin'
;
import
AnnotationSync
from
'../annotation-sync'
;
import
Bridge
from
'../../shared/bridge'
;
import
Discovery
from
'../../shared/discovery'
;
import
*
as
frameUtil
from
'../util/frame-util'
;
import
FrameObserver
from
'../frame-observer'
;
/**
* @typedef {import('../../types/annotator').AnnotationData} AnnotationData
*/
/**
* `CrossFrame` provides a connection from the annotator to the sidebar.
*
* It can be used to publish events to and subscribe to events from the sidebar.
*
* This class also has logic for injecting Hypothesis into iframes that
* are added to the page if they have the `enable-annotation` attribute set
* and are same-origin with the current document.
*/
export
default
class
CrossFrame
extends
Plugin
{
constructor
(
element
,
options
)
{
super
(
element
,
options
);
const
{
config
,
server
,
on
,
emit
}
=
options
;
const
discovery
=
new
Discovery
(
window
,
{
server
});
const
bridge
=
new
Bridge
();
const
annotationSync
=
new
AnnotationSync
(
bridge
,
{
on
,
emit
});
const
frameObserver
=
new
FrameObserver
(
element
);
const
frameIdentifiers
=
new
Map
();
/**
* Inject Hypothesis into a newly-discovered iframe.
*/
const
injectToFrame
=
frame
=>
{
if
(
!
frameUtil
.
hasHypothesis
(
frame
))
{
const
{
clientUrl
}
=
config
;
frameUtil
.
isLoaded
(
frame
,
()
=>
{
const
subFrameIdentifier
=
discovery
.
generateToken
();
frameIdentifiers
.
set
(
frame
,
subFrameIdentifier
);
const
injectedConfig
=
Object
.
assign
({},
config
,
{
subFrameIdentifier
,
});
frameUtil
.
injectHypothesis
(
frame
,
clientUrl
,
injectedConfig
);
});
}
};
const
iframeUnloaded
=
frame
=>
{
bridge
.
call
(
'destroyFrame'
,
frameIdentifiers
.
get
(
frame
));
frameIdentifiers
.
delete
(
frame
);
};
/**
* Initiate the connection to the sidebar.
*/
this
.
pluginInit
=
()
=>
{
const
onDiscoveryCallback
=
(
source
,
origin
,
token
)
=>
bridge
.
createChannel
(
source
,
origin
,
token
);
discovery
.
startDiscovery
(
onDiscoveryCallback
);
frameObserver
.
observe
(
injectToFrame
,
iframeUnloaded
);
};
/**
* Remove the connection between the sidebar and annotator.
*/
this
.
destroy
=
()
=>
{
bridge
.
destroy
();
discovery
.
stopDiscovery
();
frameObserver
.
disconnect
();
super
.
destroy
();
};
/**
* Notify the sidebar about new annotations created in the page.
*
* @param {AnnotationData[]} annotations
*/
this
.
sync
=
annotations
=>
annotationSync
.
sync
(
annotations
);
/**
* Subscribe to an event from the sidebar.
*
* @param {string} event
* @param {Function} callback
*/
this
.
on
=
(
event
,
callback
)
=>
bridge
.
on
(
event
,
callback
);
/**
* Call an RPC method exposed by the sidebar to the annotator.
*
* @param {string} method
* @param {any[]} args
*/
this
.
call
=
(
method
,
...
args
)
=>
bridge
.
call
(
method
,
...
args
);
/**
* Register a callback to be invoked once the connection to the sidebar
* is set up.
*
* @param {Function} callback
*/
this
.
onConnect
=
callback
=>
bridge
.
onConnect
(
callback
);
}
}
src/annotator/plugin/test/cross-frame-test.coffee
deleted
100644 → 0
View file @
2e0eee75
$
=
require
(
'jquery'
)
Plugin
=
require
(
'../../plugin'
)
CrossFrame
=
require
(
'../cross-frame'
)
{
$imports
}
=
require
(
'../cross-frame'
)
describe
'CrossFrame'
,
->
fakeDiscovery
=
null
fakeBridge
=
null
fakeAnnotationSync
=
null
proxyDiscovery
=
null
proxyBridge
=
null
proxyAnnotationSync
=
null
sandbox
=
sinon
.
createSandbox
()
createCrossFrame
=
(
options
)
->
defaults
=
config
:
{}
on
:
sandbox
.
stub
()
emit
:
sandbox
.
stub
()
element
=
document
.
createElement
(
'div'
)
return
new
CrossFrame
(
element
,
$
.
extend
({},
defaults
,
options
))
beforeEach
->
fakeDiscovery
=
startDiscovery
:
sandbox
.
stub
()
stopDiscovery
:
sandbox
.
stub
()
fakeBridge
=
destroy
:
sandbox
.
stub
()
createChannel
:
sandbox
.
stub
()
onConnect
:
sandbox
.
stub
()
call
:
sandbox
.
stub
()
on
:
sandbox
.
stub
()
fakeAnnotationSync
=
sync
:
sandbox
.
stub
()
proxyAnnotationSync
=
sandbox
.
stub
().
returns
(
fakeAnnotationSync
)
proxyDiscovery
=
sandbox
.
stub
().
returns
(
fakeDiscovery
)
proxyBridge
=
sandbox
.
stub
().
returns
(
fakeBridge
)
$imports
.
$mock
({
'../plugin'
:
Plugin
,
'../annotation-sync'
:
proxyAnnotationSync
,
'../../shared/bridge'
:
proxyBridge
,
'../../shared/discovery'
:
proxyDiscovery
})
afterEach
->
sandbox
.
restore
()
$imports
.
$restore
()
describe
'CrossFrame constructor'
,
->
it
'instantiates the Discovery component'
,
->
createCrossFrame
()
assert
.
calledWith
(
proxyDiscovery
,
window
)
it
'passes the options along to the bridge'
,
->
createCrossFrame
(
server
:
true
)
assert
.
calledWith
(
proxyDiscovery
,
window
,
server
:
true
)
it
'instantiates the CrossFrame component'
,
->
createCrossFrame
()
assert
.
calledWith
(
proxyDiscovery
)
it
'instantiates the AnnotationSync component'
,
->
createCrossFrame
()
assert
.
called
(
proxyAnnotationSync
)
it
'passes along options to AnnotationSync'
,
->
createCrossFrame
()
assert
.
calledWith
(
proxyAnnotationSync
,
fakeBridge
,
{
on
:
sinon
.
match
.
func
emit
:
sinon
.
match
.
func
})
describe
'.pluginInit'
,
->
it
'starts the discovery of new channels'
,
->
bridge
=
createCrossFrame
()
bridge
.
pluginInit
()
assert
.
called
(
fakeDiscovery
.
startDiscovery
)
it
'creates a channel when a new frame is discovered'
,
->
bridge
=
createCrossFrame
()
bridge
.
pluginInit
()
fakeDiscovery
.
startDiscovery
.
yield
(
'SOURCE'
,
'ORIGIN'
,
'TOKEN'
)
assert
.
called
(
fakeBridge
.
createChannel
)
assert
.
calledWith
(
fakeBridge
.
createChannel
,
'SOURCE'
,
'ORIGIN'
,
'TOKEN'
)
describe
'.destroy'
,
->
it
'stops the discovery of new frames'
,
->
cf
=
createCrossFrame
()
cf
.
destroy
()
assert
.
called
(
fakeDiscovery
.
stopDiscovery
)
it
'destroys the bridge object'
,
->
cf
=
createCrossFrame
()
cf
.
destroy
()
assert
.
called
(
fakeBridge
.
destroy
)
describe
'.sync'
,
->
it
'syncs the annotations with the other frame'
,
->
bridge
=
createCrossFrame
()
bridge
.
sync
()
assert
.
called
(
fakeAnnotationSync
.
sync
)
describe
'.on'
,
->
it
'proxies the call to the bridge'
,
->
bridge
=
createCrossFrame
()
bridge
.
on
(
'event'
,
'arg'
)
assert
.
calledWith
(
fakeBridge
.
on
,
'event'
,
'arg'
)
describe
'.call'
,
->
it
'proxies the call to the bridge'
,
->
bridge
=
createCrossFrame
()
bridge
.
call
(
'method'
,
'arg1'
,
'arg2'
)
assert
.
calledWith
(
fakeBridge
.
call
,
'method'
,
'arg1'
,
'arg2'
)
describe
'.onConnect'
,
->
it
'proxies the call to the bridge'
,
->
bridge
=
createCrossFrame
()
fn
=
->
bridge
.
onConnect
(
fn
)
assert
.
calledWith
(
fakeBridge
.
onConnect
,
fn
)
src/annotator/plugin/test/cross-frame-test.js
0 → 100644
View file @
397f269a
import
Plugin
from
'../../plugin'
;
import
CrossFrame
from
'../cross-frame'
;
import
{
$imports
}
from
'../cross-frame'
;
describe
(
'CrossFrame'
,
()
=>
{
let
fakeDiscovery
;
let
fakeBridge
;
let
fakeAnnotationSync
;
let
proxyDiscovery
;
let
proxyBridge
;
let
proxyAnnotationSync
;
const
createCrossFrame
=
options
=>
{
const
defaults
=
{
config
:
{},
on
:
sinon
.
stub
(),
emit
:
sinon
.
stub
(),
};
const
element
=
document
.
createElement
(
'div'
);
return
new
CrossFrame
(
element
,
{
...
defaults
,
...
options
});
};
beforeEach
(()
=>
{
fakeDiscovery
=
{
startDiscovery
:
sinon
.
stub
(),
stopDiscovery
:
sinon
.
stub
(),
};
fakeBridge
=
{
destroy
:
sinon
.
stub
(),
createChannel
:
sinon
.
stub
(),
onConnect
:
sinon
.
stub
(),
call
:
sinon
.
stub
(),
on
:
sinon
.
stub
(),
};
fakeAnnotationSync
=
{
sync
:
sinon
.
stub
()
};
proxyAnnotationSync
=
sinon
.
stub
().
returns
(
fakeAnnotationSync
);
proxyDiscovery
=
sinon
.
stub
().
returns
(
fakeDiscovery
);
proxyBridge
=
sinon
.
stub
().
returns
(
fakeBridge
);
$imports
.
$mock
({
'../plugin'
:
Plugin
,
'../annotation-sync'
:
proxyAnnotationSync
,
'../../shared/bridge'
:
proxyBridge
,
'../../shared/discovery'
:
proxyDiscovery
,
});
});
afterEach
(()
=>
{
$imports
.
$restore
();
});
describe
(
'CrossFrame constructor'
,
()
=>
{
it
(
'instantiates the Discovery component'
,
()
=>
{
createCrossFrame
();
assert
.
calledWith
(
proxyDiscovery
,
window
);
});
it
(
'passes the options along to the bridge'
,
()
=>
{
createCrossFrame
({
server
:
true
});
assert
.
calledWith
(
proxyDiscovery
,
window
,
{
server
:
true
});
});
it
(
'instantiates the CrossFrame component'
,
()
=>
{
createCrossFrame
();
assert
.
calledWith
(
proxyDiscovery
);
});
it
(
'instantiates the AnnotationSync component'
,
()
=>
{
createCrossFrame
();
assert
.
called
(
proxyAnnotationSync
);
});
it
(
'passes along options to AnnotationSync'
,
()
=>
{
createCrossFrame
();
assert
.
calledWith
(
proxyAnnotationSync
,
fakeBridge
,
{
on
:
sinon
.
match
.
func
,
emit
:
sinon
.
match
.
func
,
});
});
});
describe
(
'#pluginInit'
,
()
=>
{
it
(
'starts the discovery of new channels'
,
()
=>
{
const
bridge
=
createCrossFrame
();
bridge
.
pluginInit
();
assert
.
called
(
fakeDiscovery
.
startDiscovery
);
});
it
(
'creates a channel when a new frame is discovered'
,
()
=>
{
const
bridge
=
createCrossFrame
();
bridge
.
pluginInit
();
fakeDiscovery
.
startDiscovery
.
yield
(
'SOURCE'
,
'ORIGIN'
,
'TOKEN'
);
assert
.
called
(
fakeBridge
.
createChannel
);
assert
.
calledWith
(
fakeBridge
.
createChannel
,
'SOURCE'
,
'ORIGIN'
,
'TOKEN'
);
});
});
describe
(
'#destroy'
,
()
=>
{
it
(
'stops the discovery of new frames'
,
()
=>
{
const
cf
=
createCrossFrame
();
cf
.
destroy
();
assert
.
called
(
fakeDiscovery
.
stopDiscovery
);
});
it
(
'destroys the bridge object'
,
()
=>
{
const
cf
=
createCrossFrame
();
cf
.
destroy
();
assert
.
called
(
fakeBridge
.
destroy
);
});
});
describe
(
'#sync'
,
()
=>
{
it
(
'syncs the annotations with the other frame'
,
()
=>
{
const
bridge
=
createCrossFrame
();
bridge
.
sync
();
assert
.
called
(
fakeAnnotationSync
.
sync
);
});
});
describe
(
'#on'
,
()
=>
{
it
(
'proxies the call to the bridge'
,
()
=>
{
const
bridge
=
createCrossFrame
();
bridge
.
on
(
'event'
,
'arg'
);
assert
.
calledWith
(
fakeBridge
.
on
,
'event'
,
'arg'
);
});
});
describe
(
'#call'
,
()
=>
{
it
(
'proxies the call to the bridge'
,
()
=>
{
const
bridge
=
createCrossFrame
();
bridge
.
call
(
'method'
,
'arg1'
,
'arg2'
);
assert
.
calledWith
(
fakeBridge
.
call
,
'method'
,
'arg1'
,
'arg2'
);
});
});
describe
(
'#onConnect'
,
()
=>
{
it
(
'proxies the call to the bridge'
,
()
=>
{
const
bridge
=
createCrossFrame
();
const
fn
=
()
=>
{};
bridge
.
onConnect
(
fn
);
assert
.
calledWith
(
fakeBridge
.
onConnect
,
fn
);
});
});
});
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