Commit 11eb5fa2 authored by Robert Knight's avatar Robert Knight

Open the sidebar on load in response to annotations fragment

If the host page's URL contains a '#annotations:' fragment,
open the sidebar automatically when the client loads.

 * Move code for reading Hypothesis configuration from
   the environment into config.js and add tests

 * Read the '#annotations' fragment from the URL and
   pass it to app.html along with any other options
   as query string parameters.
parent 76478213
'use strict';
var docs = 'https://h.readthedocs.org/en/latest/hacking/customized-embedding.html';
/**
* Reads the Hypothesis configuration from the environment.
*
* @param {Window} window_ - The Window object to read config from.
*/
function config(window_) {
var options = {
app: window_.
document.querySelector('link[type="application/annotator+html"]').href,
};
if (window_.hasOwnProperty('hypothesisConfig')) {
if (typeof window_.hypothesisConfig === 'function') {
Object.assign(options, window_.hypothesisConfig());
} else {
throw new TypeError('hypothesisConfig must be a function, see: ' + docs);
}
}
var annotFragmentMatch = window_.location.hash.match(/^#annotations:(.*)/);
if (annotFragmentMatch) {
options.annotations = annotFragmentMatch[1];
}
return options;
}
module.exports = config;
Annotator = require('annotator') Annotator = require('annotator')
queryString = require('query-string')
$ = Annotator.$ $ = Annotator.$
Guest = require('./guest') Guest = require('./guest')
module.exports = class Host extends Guest module.exports = class Host extends Guest
constructor: (element, options) -> constructor: (element, options) ->
if options.firstRun appOpts = queryString.stringify(
options.app += (if '?' in options.app then '&' else '?') + 'firstrun' Object.assign({}, options, {app: undefined})
)
options.app += (if '?' in options.app then '&' else '?') + appOpts
# Create the iframe # Create the iframe
app = $('<iframe></iframe>') app = $('<iframe></iframe>')
......
'use strict';
require('../polyfills'); require('../polyfills');
var extend = require('extend');
var Annotator = require('annotator'); var Annotator = require('annotator');
// Polyfills // Polyfills
...@@ -32,23 +33,11 @@ Annotator.Plugin.CrossFrame.AnnotationSync = require('../annotation-sync'); ...@@ -32,23 +33,11 @@ Annotator.Plugin.CrossFrame.AnnotationSync = require('../annotation-sync');
Annotator.Plugin.CrossFrame.Bridge = require('../bridge'); Annotator.Plugin.CrossFrame.Bridge = require('../bridge');
Annotator.Plugin.CrossFrame.Discovery = require('../discovery'); Annotator.Plugin.CrossFrame.Discovery = require('../discovery');
var docs = 'https://h.readthedocs.org/en/latest/hacking/customized-embedding.html';
var appLinkEl = var appLinkEl =
document.querySelector('link[type="application/annotator+html"]'); document.querySelector('link[type="application/annotator+html"]');
var options = { var options = require('./config')(window);
app: appLinkEl.href,
};
if (window.hasOwnProperty('hypothesisConfig')) {
if (typeof window.hypothesisConfig === 'function') {
extend(options, window.hypothesisConfig());
} else {
throw new TypeError('hypothesisConfig must be a function, see: ' + docs);
}
}
Annotator.noConflict().$.noConflict(true)(function() { Annotator.noConflict().$.noConflict(true)(function() {
'use strict';
var Klass = window.PDFViewerApplication ? var Klass = window.PDFViewerApplication ?
Annotator.PdfSidebar : Annotator.PdfSidebar :
Annotator.Sidebar; Annotator.Sidebar;
......
...@@ -24,7 +24,7 @@ module.exports = class Sidebar extends Host ...@@ -24,7 +24,7 @@ module.exports = class Sidebar extends Host
super super
this.hide() this.hide()
if options.firstRun if options.firstRun || options.annotations
this.on 'panelReady', => this.show() this.on 'panelReady', => this.show()
if @plugins.BucketBar? if @plugins.BucketBar?
......
'use strict';
var config = require('../config');
describe('annotator configuration', function () {
var fakeWindowBase = {
document: {
querySelector: sinon.stub().returns({href: 'app.html'}),
},
location: {hash: ''},
};
it('reads the app src from the link tag', function () {
var linkEl = document.createElement('link');
linkEl.type = 'application/annotator+html';
linkEl.href = 'https://test.hypothes.is/app.html';
document.head.appendChild(linkEl);
assert.deepEqual(config(window), {
app: linkEl.href,
});
document.head.removeChild(linkEl);
});
it('reads the #annotation query fragment', function () {
var fakeWindow = Object.assign({}, fakeWindowBase, {
location: {hash:'#annotations:456'},
});
assert.deepEqual(config(fakeWindow), {
app: 'app.html',
annotations: '456',
});
});
it('merges the config from hypothesisConfig()', function () {
var fakeWindow = Object.assign({}, fakeWindowBase, {
hypothesisConfig: function () {
return {firstRun: true};
},
});
assert.deepEqual(config(fakeWindow), {
app: 'app.html',
firstRun: true,
});
});
});
...@@ -84,3 +84,11 @@ describe 'Host', -> ...@@ -84,3 +84,11 @@ describe 'Host', ->
assert.isTrue(host.visibleHighlights) assert.isTrue(host.visibleHighlights)
done() done()
host.publish('panelReady') host.publish('panelReady')
it 'passes options to the sidebar iframe', ->
appURL = 'http://localhost:1000/app.html'
host = createHost({
app: appURL,
annotations: '1234'
})
assert.equal(host.frame[0].children[0].src, appURL + '?annotations=1234')
...@@ -24,7 +24,7 @@ function authStateFromUserID(userid) { ...@@ -24,7 +24,7 @@ function authStateFromUserID(userid) {
module.exports = function AppController( module.exports = function AppController(
$controller, $document, $location, $rootScope, $route, $scope, $controller, $document, $location, $rootScope, $route, $scope,
$window, annotationUI, auth, drafts, features, groups, $window, annotationUI, auth, drafts, features, groups,
session session, settings
) { ) {
$controller('AnnotationUIController', {$scope: $scope}); $controller('AnnotationUIController', {$scope: $scope});
...@@ -42,8 +42,6 @@ module.exports = function AppController( ...@@ -42,8 +42,6 @@ module.exports = function AppController(
// Allow all child scopes access to the session // Allow all child scopes access to the session
$scope.session = session; $scope.session = session;
var isFirstRun = $location.search().hasOwnProperty('firstrun');
// App dialogs // App dialogs
$scope.accountDialog = {visible: false}; $scope.accountDialog = {visible: false};
$scope.shareDialog = {visible: false}; $scope.shareDialog = {visible: false};
...@@ -73,7 +71,7 @@ module.exports = function AppController( ...@@ -73,7 +71,7 @@ module.exports = function AppController(
// update the auth info in the top bar and show the login form // update the auth info in the top bar and show the login form
// after first install of the extension. // after first install of the extension.
$scope.auth = authStateFromUserID(state.userid); $scope.auth = authStateFromUserID(state.userid);
if (!state.userid && isFirstRun) { if (!state.userid && settings.firstRun) {
$scope.login(); $scope.login();
} }
}); });
......
'use strict'; 'use strict';
require('./polyfills'); require('./polyfills');
var queryString = require('query-string');
// Initialize Raven. This is required at the top of this file // Initialize Raven. This is required at the top of this file
// so that it happens early in the app's startup flow // so that it happens early in the app's startup flow
var settings = require('./settings')(document); var settings = require('./settings')(document);
Object.assign(settings, queryString.parse(window.location.search));
if (settings.raven) { if (settings.raven) {
var raven = require('./raven'); var raven = require('./raven');
raven.init(settings.raven); raven.init(settings.raven);
......
...@@ -15,6 +15,7 @@ describe('AppController', function () { ...@@ -15,6 +15,7 @@ describe('AppController', function () {
var fakeLocation = null; var fakeLocation = null;
var fakeParams = null; var fakeParams = null;
var fakeSession = null; var fakeSession = null;
var fakeSettings = null;
var fakeGroups = null; var fakeGroups = null;
var fakeRoute = null; var fakeRoute = null;
var fakeSettings = null; var fakeSettings = null;
......
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