-
Notifications
You must be signed in to change notification settings - Fork 422
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split host / sidebar and clean up plugin loading
- Loading branch information
Showing
15 changed files
with
366 additions
and
332 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,44 @@ | ||
Customized embedding | ||
#################### | ||
|
||
To customize the plugins that are loaded, define a function ``window.hypothesisConfig`` | ||
which returns an options object:: | ||
To customize the plugins that are loaded, define a function | ||
``window.hypothesisConfig`` which returns an options object:: | ||
|
||
|
||
window.hypothesisConfig = function () { | ||
return { | ||
app: 'https://example.com/custom_sidebar_iframe', | ||
Toolbar: {container: '.toolbar-wrapper'}, | ||
BucketBar: {container: '.bucketbar-wrapper'} | ||
constructor: Annotator.Sidebar, | ||
app: 'https://example.com/custom_sidebar_iframe' | ||
}; | ||
}; | ||
|
||
In the above example, the Toolbar will be attached to the element with the | ||
``.toolbar-wrapper`` class, and the BucketBar to the element with the ``.bucketbar-wrapper`` | ||
class. | ||
The ``constructor`` property should be used to select an annotation | ||
application. Four are provided: ``Annotator.Guest``, ``Annotator.Host``, | ||
``Annotator.Sidebar`` and ``Annotator.PdfSidebar``. | ||
|
||
The full range of possibilities here is still in need of documentation and we | ||
would appreciate any help to improve that. | ||
``Annotator.Guest`` expects to connect to an annotator widget running in a | ||
different frame. Any number of instances can communicate with a single widget | ||
in order to provide annotation of many frames. | ||
|
||
With the exception of ``app`` and ``constructor``, the properties for the options object | ||
are the names of Annotator plugins and their values are the options passed to the individual | ||
plugin constructors. | ||
``Annotator.Host`` is an extended version of ``Annotator.Guest`` that will | ||
instantiate an annotator widget by loading the location given by the ``app`` | ||
option in an iframe and appending it to the document. | ||
|
||
The ``app`` property should be a url pointing to the HTML document that will be | ||
embedded in the page. | ||
``Annotator.Sidebar`` is an extended ``Annotator.Host`` that puts the widget | ||
in a sidebar interface. It loads additional plugins that show a bar of bucket | ||
indicators, each providing the ability to select a cluster of highlights, and a | ||
toolbar that can be used to resize the widget and control other aspects of the | ||
user interface. | ||
|
||
The ``constructor`` property should be used in when you want to annotate an iframe on a host | ||
document. By instantiating the ``Annotator.Guest`` class inside the iframe you can capture | ||
selection data from the frame which will be accessible by a host annotator in a parent document. | ||
By default, Hypothesis instantiates the ``Annotator.Host`` class defined in the injected code | ||
loaded by ``embed.js``. It is possible to change this by assigning an alternate ``constructor`` | ||
in the options object returned by ``window.hypothesisConfig``. For example:: | ||
``Annotator.PdfSidebar`` is a custom version of ``Annotator.Sidebar`` with | ||
defaults tailored for use in a PDF.js viewer. | ||
|
||
It is possible to use a custom application by assigning an alternate | ||
``constructor`` in the options object returned by | ||
``window.hypothesisConfig``. For example:: | ||
|
||
window.hypothesisConfig = function () { | ||
return { | ||
constructor: Annotator.Guest | ||
}; | ||
}; | ||
|
||
An Annotator Host can connect to multiple guests. | ||
window.hypothesisConfig = function () { | ||
return { | ||
constructor: Annotator.Guest | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,170 +1,36 @@ | ||
raf = require('raf') | ||
|
||
Annotator = require('annotator') | ||
$ = Annotator.$ | ||
|
||
Hammer = require('hammerjs') | ||
|
||
Guest = require('./guest') | ||
|
||
# Minimum width to which the frame can be resized. | ||
MIN_RESIZE = 280 | ||
|
||
|
||
module.exports = class Host extends Guest | ||
renderFrame: null | ||
gestureState: null | ||
|
||
constructor: (element, options) -> | ||
src = options.app | ||
if options.firstRun | ||
# Allow options.app to contain query string params. | ||
src = src + (if '?' in src then '&' else '?') + 'firstrun' | ||
options.app += (if '?' in options.app then '&' else '?') + 'firstrun' | ||
|
||
# Create the iframe | ||
app = $('<iframe></iframe>') | ||
.attr('name', 'hyp_sidebar_frame') | ||
.attr('seamless', '') | ||
.attr('src', src) | ||
.attr('src', options.app) | ||
|
||
@frame = $('<div></div>') | ||
.css('display', 'none') | ||
.addClass('annotator-frame annotator-outer annotator-collapsed') | ||
.addClass('annotator-frame annotator-outer') | ||
.appendTo(element) | ||
|
||
super | ||
this._addCrossFrameListeners() | ||
|
||
app.appendTo(@frame) | ||
|
||
if options.firstRun | ||
this.on 'panelReady', => this.showFrame(transition: false) | ||
|
||
# Host frame dictates the toolbar options. | ||
this.on 'panelReady', => | ||
# Initialize tool state. | ||
this.setVisibleHighlights(!!options.showHighlights) | ||
|
||
# Time to actually show the UI | ||
# Show the UI | ||
@frame.css('display', '') | ||
|
||
if @plugins.BucketBar? | ||
this._setupGestures() | ||
@plugins.BucketBar.element.on 'click', (event) => | ||
if @frame.hasClass 'annotator-collapsed' | ||
this.showFrame() | ||
|
||
destroy: -> | ||
@frame.remove() | ||
super | ||
|
||
showFrame: (options={transition: true}) -> | ||
if options.transition | ||
@frame.removeClass 'annotator-no-transition' | ||
else | ||
@frame.addClass 'annotator-no-transition' | ||
@frame.css 'margin-left': "#{-1 * @frame.width()}px" | ||
@frame.removeClass 'annotator-collapsed' | ||
|
||
if @toolbar? | ||
@toolbar.find('[name=sidebar-toggle]') | ||
.removeClass('h-icon-chevron-left') | ||
.addClass('h-icon-chevron-right') | ||
|
||
hideFrame: -> | ||
@frame.css 'margin-left': '' | ||
@frame.removeClass 'annotator-no-transition' | ||
@frame.addClass 'annotator-collapsed' | ||
|
||
if @toolbar? | ||
@toolbar.find('[name=sidebar-toggle]') | ||
.removeClass('h-icon-chevron-right') | ||
.addClass('h-icon-chevron-left') | ||
|
||
_addCrossFrameListeners: -> | ||
@crossframe.on('showFrame', this.showFrame.bind(this, null)) | ||
@crossframe.on('hideFrame', this.hideFrame.bind(this, null)) | ||
|
||
_initializeGestureState: -> | ||
@gestureState = | ||
initial: null | ||
final: null | ||
|
||
onPan: (event) => | ||
switch event.type | ||
when 'panstart' | ||
# Initialize the gesture state | ||
this._initializeGestureState() | ||
# Immadiate response | ||
@frame.addClass 'annotator-no-transition' | ||
# Escape iframe capture | ||
@frame.css('pointer-events', 'none') | ||
# Set origin margin | ||
@gestureState.initial = parseInt(getComputedStyle(@frame[0]).marginLeft) | ||
|
||
when 'panend' | ||
# Re-enable transitions | ||
@frame.removeClass 'annotator-no-transition' | ||
# Re-enable iframe events | ||
@frame.css('pointer-events', '') | ||
# Snap open or closed | ||
if @gestureState.final <= -MIN_RESIZE | ||
this.showFrame() | ||
else | ||
this.hideFrame() | ||
# Reset the gesture state | ||
this._initializeGestureState() | ||
|
||
when 'panleft', 'panright' | ||
return unless @gestureState.initial? | ||
# Compute new margin from delta and initial conditions | ||
m = @gestureState.initial | ||
d = event.deltaX | ||
@gestureState.final = Math.min(Math.round(m + d), 0) | ||
# Start updating | ||
this._updateLayout() | ||
|
||
onSwipe: (event) => | ||
switch event.type | ||
when 'swipeleft' | ||
this.showFrame() | ||
when 'swiperight' | ||
this.hideFrame() | ||
|
||
_setupGestures: -> | ||
$toggle = @toolbar.find('[name=sidebar-toggle]') | ||
|
||
# Prevent any default gestures on the handle | ||
$toggle.on('touchmove', (event) -> event.preventDefault()) | ||
|
||
# Set up the Hammer instance and handlers | ||
mgr = new Hammer.Manager($toggle[0]) | ||
.on('panstart panend panleft panright', this.onPan) | ||
.on('swipeleft swiperight', this.onSwipe) | ||
|
||
# Set up the gesture recognition | ||
pan = mgr.add(new Hammer.Pan({direction: Hammer.DIRECTION_HORIZONTAL})) | ||
swipe = mgr.add(new Hammer.Swipe({direction: Hammer.DIRECTION_HORIZONTAL})) | ||
swipe.recognizeWith(pan) | ||
|
||
# Set up the initial state | ||
this._initializeGestureState() | ||
|
||
# Return this for chaining | ||
this | ||
|
||
# Schedule any changes needed to update the layout of the widget or page | ||
# in response to interface changes. | ||
_updateLayout: -> | ||
# Only schedule one frame at a time | ||
return if @renderFrame | ||
|
||
# Schedule a frame | ||
@renderFrame = raf => | ||
@renderFrame = null # Clear the schedule | ||
|
||
# Process the resize gesture | ||
if @gestureState.final isnt @gestureState.initial | ||
m = @gestureState.final | ||
w = -m | ||
@frame.css('margin-left', "#{m}px") | ||
if w >= MIN_RESIZE then @frame.css('width', "#{w}px") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.