Commit 33067d0d authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Add config support for Notebook feature

Add env NOTEBOOK_APP_URL and support for it in
build and config for annotator.
parent fdc5a1bd
...@@ -224,6 +224,10 @@ let isFirstBuild = true; ...@@ -224,6 +224,10 @@ let isFirstBuild = true;
function generateBootScript(manifest, { usingDevServer = false } = {}) { function generateBootScript(manifest, { usingDevServer = false } = {}) {
const { version } = require('./package.json'); const { version } = require('./package.json');
const defaultNotebookAppUrl = process.env.NOTEBOOK_APP_URL
? `${process.env.NOTEBOOK_APP_URL}`
: '{current_scheme}://{current_host}:5000/notebook';
const defaultSidebarAppUrl = process.env.SIDEBAR_APP_URL const defaultSidebarAppUrl = process.env.SIDEBAR_APP_URL
? `${process.env.SIDEBAR_APP_URL}` ? `${process.env.SIDEBAR_APP_URL}`
: '{current_scheme}://{current_host}:5000/app.html'; : '{current_scheme}://{current_host}:5000/app.html';
...@@ -239,6 +243,7 @@ function generateBootScript(manifest, { usingDevServer = false } = {}) { ...@@ -239,6 +243,7 @@ function generateBootScript(manifest, { usingDevServer = false } = {}) {
if (isFirstBuild) { if (isFirstBuild) {
log(`Sidebar app URL: ${defaultSidebarAppUrl}`); log(`Sidebar app URL: ${defaultSidebarAppUrl}`);
log(`Notebook app URL: ${defaultNotebookAppUrl}`);
log(`Client asset root URL: ${defaultAssetRoot}`); log(`Client asset root URL: ${defaultAssetRoot}`);
isFirstBuild = false; isFirstBuild = false;
} }
...@@ -247,6 +252,7 @@ function generateBootScript(manifest, { usingDevServer = false } = {}) { ...@@ -247,6 +252,7 @@ function generateBootScript(manifest, { usingDevServer = false } = {}) {
.src('build/scripts/boot.bundle.js') .src('build/scripts/boot.bundle.js')
.pipe(replace('__MANIFEST__', JSON.stringify(manifest))) .pipe(replace('__MANIFEST__', JSON.stringify(manifest)))
.pipe(replace('__ASSET_ROOT__', defaultAssetRoot)) .pipe(replace('__ASSET_ROOT__', defaultAssetRoot))
.pipe(replace('__NOTEBOOK_APP_URL__', defaultNotebookAppUrl))
.pipe(replace('__SIDEBAR_APP_URL__', defaultSidebarAppUrl)) .pipe(replace('__SIDEBAR_APP_URL__', defaultSidebarAppUrl))
// Strip sourcemap link. It will have been invalidated by the previous // Strip sourcemap link. It will have been invalidated by the previous
// replacements and the bundle is so small that it isn't really valuable. // replacements and the bundle is so small that it isn't really valuable.
......
...@@ -39,6 +39,7 @@ export default function configFrom(window_) { ...@@ -39,6 +39,7 @@ export default function configFrom(window_) {
requestConfigFromFrame: settings.hostPageSetting('requestConfigFromFrame'), requestConfigFromFrame: settings.hostPageSetting('requestConfigFromFrame'),
services: settings.hostPageSetting('services'), services: settings.hostPageSetting('services'),
showHighlights: settings.showHighlights, showHighlights: settings.showHighlights,
notebookAppUrl: settings.notebookAppUrl,
sidebarAppUrl: settings.sidebarAppUrl, sidebarAppUrl: settings.sidebarAppUrl,
// Subframe identifier given when a frame is being embedded into // Subframe identifier given when a frame is being embedded into
// by a top level client // by a top level client
......
...@@ -8,33 +8,33 @@ export default function settingsFrom(window_) { ...@@ -8,33 +8,33 @@ export default function settingsFrom(window_) {
const configFuncSettings = configFuncSettingsFrom(window_); const configFuncSettings = configFuncSettingsFrom(window_);
/** /**
* Return the href URL of the first annotator sidebar link in the given document. * Return the href of the first annotator link in the given
* document with this `rel` attribute.
* *
* Return the value of the href attribute of the first * Return the value of the href attribute of the first
* `<link type="application/annotator+html" rel="sidebar">` element in the given document. * `<link type="application/annotator+html" rel="${rel}">`
* * element in the given document. This URL is used as the `src` for sidebar
* This URL is used as the src of the sidebar's iframe. * or notebook iframes.
* *
* @return {string} - The URL to use for the sidebar's iframe. * @param {string} rel - The `rel` attribute to match
* * @return {string} - The URL to use for the iframe
* @throws {Error} - If there's no annotator link or the first annotator has * @throws {Error} - If there's no link with the `rel` indicated, or the first
* no href. * matching link has no `href`
*
*/ */
function sidebarAppUrl() { function urlFromLinkTag(rel) {
const link = window_.document.querySelector( const link = window_.document.querySelector(
'link[type="application/annotator+html"][rel="sidebar"]' `link[type="application/annotator+html"][rel="${rel}"]`
); );
if (!link) { if (!link) {
throw new Error( throw new Error(
'No application/annotator+html (rel="sidebar") link in the document' `No application/annotator+html (rel="${rel}") link in the document`
); );
} }
if (!link.href) { if (!link.href) {
throw new Error( throw new Error(
'application/annotator+html (rel="sidebar") link has no href' `application/annotator+html (rel="${rel}") link has no href`
); );
} }
...@@ -45,13 +45,13 @@ export default function settingsFrom(window_) { ...@@ -45,13 +45,13 @@ export default function settingsFrom(window_) {
* Return the href URL of the first annotator client link in the given document. * Return the href URL of the first annotator client link in the given document.
* *
* Return the value of the href attribute of the first * Return the value of the href attribute of the first
* `<link type="application/annotator+html" rel="hypothesis-client">` element in the given document. * `<link type="application/annotator+javascript" rel="hypothesis-client">`
* element in the given document.
* *
* This URL is used to identify where the client is from and what url should be * This URL is used to identify where the client is from and what url should
* used inside of subframes * be used inside of subframes.
* *
* @return {string} - The URL that the client is hosted from * @return {string} - The URL that the client is hosted from
*
* @throws {Error} - If there's no annotator link or the first annotator has * @throws {Error} - If there's no annotator link or the first annotator has
* no href. * no href.
* *
...@@ -177,7 +177,7 @@ export default function settingsFrom(window_) { ...@@ -177,7 +177,7 @@ export default function settingsFrom(window_) {
const coerceValue = const coerceValue =
typeof options.coerce === 'function' ? options.coerce : name => name; typeof options.coerce === 'function' ? options.coerce : name => name;
if (!allowInBrowserExt && isBrowserExtension(sidebarAppUrl())) { if (!allowInBrowserExt && isBrowserExtension(urlFromLinkTag('sidebar'))) {
return hasDefaultValue ? options.defaultValue : null; return hasDefaultValue ? options.defaultValue : null;
} }
...@@ -206,11 +206,14 @@ export default function settingsFrom(window_) { ...@@ -206,11 +206,14 @@ export default function settingsFrom(window_) {
get group() { get group() {
return group(); return group();
}, },
get notebookAppUrl() {
return urlFromLinkTag('notebook');
},
get showHighlights() { get showHighlights() {
return showHighlights(); return showHighlights();
}, },
get sidebarAppUrl() { get sidebarAppUrl() {
return sidebarAppUrl(); return urlFromLinkTag('sidebar');
}, },
get query() { get query() {
return query(); return query();
......
import settingsFrom from '../settings'; import settingsFrom from '../settings';
import { $imports } from '../settings'; import { $imports } from '../settings';
describe('annotator.config.settingsFrom', function () { describe('annotator/config/settingsFrom', () => {
let fakeConfigFuncSettingsFrom; let fakeConfigFuncSettingsFrom;
let fakeIsBrowserExtension; let fakeIsBrowserExtension;
let fakeParseJsonConfig; let fakeParseJsonConfig;
...@@ -22,90 +22,169 @@ describe('annotator.config.settingsFrom', function () { ...@@ -22,90 +22,169 @@ describe('annotator.config.settingsFrom', function () {
$imports.$restore(); $imports.$restore();
}); });
describe('#sidebarAppUrl', function () { describe('app frame URLs from link tags', () => {
function appendSidebarLinkToDocument(href) { function appendLink(href, rel) {
const link = document.createElement('link'); const link = document.createElement('link');
link.type = 'application/annotator+html'; link.type = 'application/annotator+html';
link.rel = 'sidebar'; link.rel = rel;
if (href) { if (href) {
link.href = href; link.href = href;
} }
document.head.appendChild(link); document.head.appendChild(link);
return link; return link;
} }
describe('#notebookAppUrl', () => {
context(
"when there's an application/annotator+html notebook link",
() => {
let link;
beforeEach(
'add an application/annotator+html notebook <link>',
() => {
link = appendLink('http://example.com/app.html', 'notebook');
}
);
context("when there's an application/annotator+html link", function () { afterEach('tidy up the notebook link', () => {
let link; link.remove();
});
beforeEach('add an application/annotator+html <link>', function () { it('returns the href from the notebook link', () => {
link = appendSidebarLinkToDocument('http://example.com/app.html'); assert.equal(
}); settingsFrom(window).notebookAppUrl,
'http://example.com/app.html'
);
});
}
);
context('when there are multiple annotator+html notebook links', () => {
let link1;
let link2;
beforeEach('add two notebook links to the document', () => {
link1 = appendLink('http://example.com/app1', 'notebook');
link2 = appendLink('http://example.com/app2', 'notebook');
});
afterEach('tidy up the link', function () { afterEach('tidy up the notebook links', () => {
document.head.removeChild(link); link1.remove();
link2.remove();
});
it('returns the href from the first notebook link found', () => {
assert.equal(
settingsFrom(window).notebookAppUrl,
'http://example.com/app1'
);
});
}); });
it('returns the href from the link', function () { context('when the annotator+html notebook link has no href', () => {
assert.equal( let link;
settingsFrom(window).sidebarAppUrl,
'http://example.com/app.html' beforeEach(
'add an application/annotator+html notebook <link> with no href',
() => {
link = appendLink(undefined, 'notebook');
}
); );
});
});
context('when there are multiple annotator+html links', function () { afterEach('tidy up the notebook link', () => {
let link1; link.remove();
let link2; });
beforeEach('add two links to the document', function () { it('throws an error', () => {
link1 = appendSidebarLinkToDocument('http://example.com/app1'); assert.throws(() => {
link2 = appendSidebarLinkToDocument('http://example.com/app2'); settingsFrom(window).notebookAppUrl; // eslint-disable-line no-unused-expressions
}, 'application/annotator+html (rel="notebook") link has no href');
});
}); });
afterEach('tidy up the links', function () { context("when there's no annotator+html notebook link", () => {
document.head.removeChild(link1); it('throws an error', () => {
document.head.removeChild(link2); assert.throws(() => {
settingsFrom(window).notebookAppUrl; // eslint-disable-line no-unused-expressions
}, 'No application/annotator+html (rel="notebook") link in the document');
});
}); });
});
it('returns the href from the first one', function () { describe('#sidebarAppUrl', () => {
assert.equal( context("when there's an application/annotator+html sidebar link", () => {
settingsFrom(window).sidebarAppUrl, let link;
'http://example.com/app1'
); beforeEach('add an application/annotator+html sidebar <link>', () => {
link = appendLink('http://example.com/app.html', 'sidebar');
});
afterEach('tidy up the sidebar link', () => {
link.remove();
});
it('returns the href from the sidebar link', () => {
assert.equal(
settingsFrom(window).sidebarAppUrl,
'http://example.com/app.html'
);
});
}); });
});
context('when the annotator+html link has no href', function () { context('when there are multiple annotator+html sidebar links', () => {
let link; let link1;
let link2;
beforeEach( beforeEach('add two sidebar links to the document', () => {
'add an application/annotator+html <link> with no href', link1 = appendLink('http://example.com/app1', 'sidebar');
function () { link2 = appendLink('http://example.com/app2', 'sidebar');
link = appendSidebarLinkToDocument(); });
}
); afterEach('tidy up the sidebar links', () => {
link1.remove();
link2.remove();
});
afterEach('tidy up the link', function () { it('returns the href from the first one', () => {
document.head.removeChild(link); assert.equal(
settingsFrom(window).sidebarAppUrl,
'http://example.com/app1'
);
});
}); });
it('throws an error', function () { context('when the annotator+html sidebar link has no href', () => {
assert.throws(function () { let link;
settingsFrom(window).sidebarAppUrl; // eslint-disable-line no-unused-expressions
}, 'application/annotator+html (rel="sidebar") link has no href'); beforeEach(
'add an application/annotator+html sidebar <link> with no href',
() => {
link = appendLink(null, 'sidebar');
}
);
afterEach('tidy up the sidebar link', () => {
link.remove();
});
it('throws an error', () => {
assert.throws(() => {
settingsFrom(window).sidebarAppUrl; // eslint-disable-line no-unused-expressions
}, 'application/annotator+html (rel="sidebar") link has no href');
});
}); });
});
context("when there's no annotator+html link", function () { context("when there's no annotator+html sidebar link", () => {
it('throws an error', function () { it('throws an error', () => {
assert.throws(function () { assert.throws(() => {
settingsFrom(window).sidebarAppUrl; // eslint-disable-line no-unused-expressions settingsFrom(window).sidebarAppUrl; // eslint-disable-line no-unused-expressions
}, 'No application/annotator+html (rel="sidebar") link in the document'); }, 'No application/annotator+html (rel="sidebar") link in the document');
});
}); });
}); });
}); });
describe('#clientUrl', function () { describe('#clientUrl', () => {
function appendClientUrlLinkToDocument(href) { function appendClientUrlLinkToDocument(href) {
const link = document.createElement('link'); const link = document.createElement('link');
link.type = 'application/annotator+javascript'; link.type = 'application/annotator+javascript';
...@@ -117,74 +196,68 @@ describe('annotator.config.settingsFrom', function () { ...@@ -117,74 +196,68 @@ describe('annotator.config.settingsFrom', function () {
return link; return link;
} }
context( context("when there's an application/annotator+javascript link", () => {
"when there's an application/annotator+javascript link", let link;
function () {
let link;
beforeEach( beforeEach('add an application/annotator+javascript <link>', () => {
'add an application/annotator+javascript <link>', link = appendClientUrlLinkToDocument('http://example.com/app.html');
function () { });
link = appendClientUrlLinkToDocument('http://example.com/app.html');
}
);
afterEach('tidy up the link', function () { afterEach('tidy up the link', () => {
document.head.removeChild(link); link.remove();
}); });
it('returns the href from the link', function () { it('returns the href from the link', () => {
assert.equal( assert.equal(
settingsFrom(window).clientUrl, settingsFrom(window).clientUrl,
'http://example.com/app.html' 'http://example.com/app.html'
); );
}); });
} });
);
context('when there are multiple annotator+javascript links', function () { context('when there are multiple annotator+javascript links', () => {
let link1; let link1;
let link2; let link2;
beforeEach('add two links to the document', function () { beforeEach('add two links to the document', () => {
link1 = appendClientUrlLinkToDocument('http://example.com/app1'); link1 = appendClientUrlLinkToDocument('http://example.com/app1');
link2 = appendClientUrlLinkToDocument('http://example.com/app2'); link2 = appendClientUrlLinkToDocument('http://example.com/app2');
}); });
afterEach('tidy up the links', function () { afterEach('tidy up the links', () => {
document.head.removeChild(link1); link1.remove();
document.head.removeChild(link2); link2.remove();
}); });
it('returns the href from the first one', function () { it('returns the href from the first one', () => {
assert.equal(settingsFrom(window).clientUrl, 'http://example.com/app1'); assert.equal(settingsFrom(window).clientUrl, 'http://example.com/app1');
}); });
}); });
context('when the annotator+javascript link has no href', function () { context('when the annotator+javascript link has no href', () => {
let link; let link;
beforeEach( beforeEach(
'add an application/annotator+javascript <link> with no href', 'add an application/annotator+javascript <link> with no href',
function () { () => {
link = appendClientUrlLinkToDocument(); link = appendClientUrlLinkToDocument();
} }
); );
afterEach('tidy up the link', function () { afterEach('tidy up the link', () => {
document.head.removeChild(link); link.remove();
}); });
it('throws an error', function () { it('throws an error', () => {
assert.throws(function () { assert.throws(() => {
settingsFrom(window).clientUrl; // eslint-disable-line no-unused-expressions settingsFrom(window).clientUrl; // eslint-disable-line no-unused-expressions
}, 'application/annotator+javascript (rel="hypothesis-client") link has no href'); }, 'application/annotator+javascript (rel="hypothesis-client") link has no href');
}); });
}); });
context("when there's no annotator+javascript link", function () { context("when there's no annotator+javascript link", () => {
it('throws an error', function () { it('throws an error', () => {
assert.throws(function () { assert.throws(() => {
settingsFrom(window).clientUrl; // eslint-disable-line no-unused-expressions settingsFrom(window).clientUrl; // eslint-disable-line no-unused-expressions
}, 'No application/annotator+javascript (rel="hypothesis-client") link in the document'); }, 'No application/annotator+javascript (rel="hypothesis-client") link in the document');
}); });
...@@ -202,20 +275,17 @@ describe('annotator.config.settingsFrom', function () { ...@@ -202,20 +275,17 @@ describe('annotator.config.settingsFrom', function () {
}; };
} }
describe('#annotations', function () { describe('#annotations', () => {
context( context(
'when the host page has a js-hypothesis-config with an annotations setting', 'when the host page has a js-hypothesis-config with an annotations setting',
function () { () => {
beforeEach( beforeEach('add a js-hypothesis-config annotations setting', () => {
'add a js-hypothesis-config annotations setting', fakeParseJsonConfig.returns({
function () { annotations: 'annotationsFromJSON',
fakeParseJsonConfig.returns({ });
annotations: 'annotationsFromJSON', });
});
}
);
it('returns the annotations from the js-hypothesis-config script', function () { it('returns the annotations from the js-hypothesis-config script', () => {
assert.equal( assert.equal(
settingsFrom(fakeWindow()).annotations, settingsFrom(fakeWindow()).annotations,
'annotationsFromJSON' 'annotationsFromJSON'
...@@ -223,11 +293,11 @@ describe('annotator.config.settingsFrom', function () { ...@@ -223,11 +293,11 @@ describe('annotator.config.settingsFrom', function () {
}); });
context( context(
"when there's also an annotations in the URL fragment", "when there's also an `annotations` in the URL fragment",
function () { () => {
specify( specify(
'js-hypothesis-config annotations override URL ones', 'js-hypothesis-config annotations override URL ones',
function () { () => {
const window_ = fakeWindow( const window_ = fakeWindow(
'http://localhost:3000#annotations:annotationsFromURL' 'http://localhost:3000#annotations:annotationsFromURL'
); );
...@@ -269,8 +339,8 @@ describe('annotator.config.settingsFrom', function () { ...@@ -269,8 +339,8 @@ describe('annotator.config.settingsFrom', function () {
returns: null, returns: null,
}, },
].forEach(function (test) { ].forEach(function (test) {
describe(test.describe, function () { describe(test.describe, () => {
it(test.it, function () { it(test.it, () => {
assert.deepEqual( assert.deepEqual(
settingsFrom(fakeWindow(test.url)).annotations, settingsFrom(fakeWindow(test.url)).annotations,
test.returns test.returns
...@@ -303,31 +373,28 @@ describe('annotator.config.settingsFrom', function () { ...@@ -303,31 +373,28 @@ describe('annotator.config.settingsFrom', function () {
}); });
}); });
describe('#query', function () { describe('#query', () => {
context( context(
'when the host page has a js-hypothesis-config with a query setting', 'when the host page has a js-hypothesis-config with a query setting',
function () { () => {
beforeEach('add a js-hypothesis-config query setting', function () { beforeEach('add a js-hypothesis-config query setting', () => {
fakeParseJsonConfig.returns({ fakeParseJsonConfig.returns({
query: 'queryFromJSON', query: 'queryFromJSON',
}); });
}); });
it('returns the query from the js-hypothesis-config script', function () { it('returns the query from the js-hypothesis-config script', () => {
assert.equal(settingsFrom(fakeWindow()).query, 'queryFromJSON'); assert.equal(settingsFrom(fakeWindow()).query, 'queryFromJSON');
}); });
context("when there's also a query in the URL fragment", function () { context("when there's also a query in the URL fragment", () => {
specify( specify('js-hypothesis-config queries override URL ones', () => {
'js-hypothesis-config queries override URL ones', const window_ = fakeWindow(
function () { 'http://localhost:3000#annotations:query:queryFromUrl'
const window_ = fakeWindow( );
'http://localhost:3000#annotations:query:queryFromUrl'
);
assert.equal(settingsFrom(window_).query, 'queryFromJSON'); assert.equal(settingsFrom(window_).query, 'queryFromJSON');
} });
);
}); });
} }
); );
...@@ -376,8 +443,8 @@ describe('annotator.config.settingsFrom', function () { ...@@ -376,8 +443,8 @@ describe('annotator.config.settingsFrom', function () {
returns: null, returns: null,
}, },
].forEach(function (test) { ].forEach(function (test) {
describe(test.describe, function () { describe(test.describe, () => {
it(test.it, function () { it(test.it, () => {
assert.deepEqual( assert.deepEqual(
settingsFrom(fakeWindow(test.url)).query, settingsFrom(fakeWindow(test.url)).query,
test.returns test.returns
...@@ -386,8 +453,8 @@ describe('annotator.config.settingsFrom', function () { ...@@ -386,8 +453,8 @@ describe('annotator.config.settingsFrom', function () {
}); });
}); });
describe('when the URL contains an invalid fragment', function () { describe('when the URL contains an invalid fragment', () => {
it('returns null', function () { it('returns null', () => {
// An invalid escape sequence which will cause decodeURIComponent() to // An invalid escape sequence which will cause decodeURIComponent() to
// throw a URIError. // throw a URIError.
const invalidFrag = '%aaaaa'; const invalidFrag = '%aaaaa';
...@@ -399,7 +466,7 @@ describe('annotator.config.settingsFrom', function () { ...@@ -399,7 +466,7 @@ describe('annotator.config.settingsFrom', function () {
}); });
}); });
describe('#showHighlights', function () { describe('#showHighlights', () => {
[ [
{ {
it: 'returns an "always" setting from the host page unmodified', it: 'returns an "always" setting from the host page unmodified',
...@@ -465,7 +532,7 @@ describe('annotator.config.settingsFrom', function () { ...@@ -465,7 +532,7 @@ describe('annotator.config.settingsFrom', function () {
output: /regex/, output: /regex/,
}, },
].forEach(function (test) { ].forEach(function (test) {
it(test.it, function () { it(test.it, () => {
fakeParseJsonConfig.returns({ fakeParseJsonConfig.returns({
showHighlights: test.input, showHighlights: test.input,
}); });
...@@ -474,7 +541,7 @@ describe('annotator.config.settingsFrom', function () { ...@@ -474,7 +541,7 @@ describe('annotator.config.settingsFrom', function () {
assert.deepEqual(settings.showHighlights, test.output); assert.deepEqual(settings.showHighlights, test.output);
}); });
it(test.it, function () { it(test.it, () => {
fakeConfigFuncSettingsFrom.returns({ fakeConfigFuncSettingsFrom.returns({
showHighlights: test.input, showHighlights: test.input,
}); });
...@@ -484,16 +551,16 @@ describe('annotator.config.settingsFrom', function () { ...@@ -484,16 +551,16 @@ describe('annotator.config.settingsFrom', function () {
}); });
}); });
it("defaults to 'always' if there's no showHighlights setting in the host page", function () { it("defaults to 'always' if there's no showHighlights setting in the host page", () => {
assert.equal(settingsFrom(fakeWindow()).showHighlights, 'always'); assert.equal(settingsFrom(fakeWindow()).showHighlights, 'always');
}); });
context('when the client is in a browser extension', function () { context('when the client is in a browser extension', () => {
beforeEach('configure a browser extension client', function () { beforeEach('configure a browser extension client', () => {
fakeIsBrowserExtension.returns(true); fakeIsBrowserExtension.returns(true);
}); });
it("doesn't read the setting from the host page, defaults to 'always'", function () { it("doesn't read the setting from the host page, defaults to 'always'", () => {
fakeParseJsonConfig.returns({ fakeParseJsonConfig.returns({
showHighlights: 'never', showHighlights: 'never',
}); });
...@@ -506,7 +573,7 @@ describe('annotator.config.settingsFrom', function () { ...@@ -506,7 +573,7 @@ describe('annotator.config.settingsFrom', function () {
}); });
}); });
describe('#hostPageSetting', function () { describe('#hostPageSetting', () => {
[ [
{ {
when: 'the client is embedded in a web page', when: 'the client is embedded in a web page',
...@@ -621,8 +688,8 @@ describe('annotator.config.settingsFrom', function () { ...@@ -621,8 +688,8 @@ describe('annotator.config.settingsFrom', function () {
expected: 'the default value', expected: 'the default value',
}, },
].forEach(function (test) { ].forEach(function (test) {
context(test.when, function () { context(test.when, () => {
specify(test.specify, function () { specify(test.specify, () => {
fakeIsBrowserExtension.returns(test.isBrowserExtension); fakeIsBrowserExtension.returns(test.isBrowserExtension);
fakeConfigFuncSettingsFrom.returns(test.configFuncSettings); fakeConfigFuncSettingsFrom.returns(test.configFuncSettings);
fakeParseJsonConfig.returns(test.jsonSettings); fakeParseJsonConfig.returns(test.jsonSettings);
......
...@@ -21,6 +21,7 @@ const commonPolyfills = [ ...@@ -21,6 +21,7 @@ const commonPolyfills = [
/** /**
* @typedef AnnotatorConfig * @typedef AnnotatorConfig
* @prop {string} assetRoot - The root URL to which URLs in `manifest` are relative * @prop {string} assetRoot - The root URL to which URLs in `manifest` are relative
* @prop {string} notebookAppUrl - The URL of the sidebar's notebook
* @prop {string} sidebarAppUrl - The URL of the sidebar's HTML page * @prop {string} sidebarAppUrl - The URL of the sidebar's HTML page
* @prop {Object.<string,string>} manifest - * @prop {Object.<string,string>} manifest -
* A mapping from canonical asset path to cache-busted asset path * A mapping from canonical asset path to cache-busted asset path
...@@ -101,6 +102,13 @@ export function bootHypothesisClient(doc, config) { ...@@ -101,6 +102,13 @@ export function bootHypothesisClient(doc, config) {
sidebarUrl.type = 'application/annotator+html'; sidebarUrl.type = 'application/annotator+html';
doc.head.appendChild(sidebarUrl); doc.head.appendChild(sidebarUrl);
// Register the URL of the notebook app which the Hypothesis client should load.
const notebookUrl = doc.createElement('link');
notebookUrl.rel = 'notebook';
notebookUrl.href = config.notebookAppUrl;
notebookUrl.type = 'application/annotator+html';
doc.head.appendChild(notebookUrl);
// Register the URL of the annotation client which is currently being used to drive // Register the URL of the annotation client which is currently being used to drive
// annotation interactions. // annotation interactions.
const clientUrl = doc.createElement('link'); const clientUrl = doc.createElement('link');
......
...@@ -26,10 +26,18 @@ if (isBrowserSupported()) { ...@@ -26,10 +26,18 @@ if (isBrowserSupported()) {
if (document.querySelector('hypothesis-app')) { if (document.querySelector('hypothesis-app')) {
bootSidebarApp(document, { assetRoot, manifest }); bootSidebarApp(document, { assetRoot, manifest });
} else { } else {
const notebookAppUrl = processUrlTemplate(
settings.notebookAppUrl || '__NOTEBOOK_APP_URL__'
);
const sidebarAppUrl = processUrlTemplate( const sidebarAppUrl = processUrlTemplate(
settings.sidebarAppUrl || '__SIDEBAR_APP_URL__' settings.sidebarAppUrl || '__SIDEBAR_APP_URL__'
); );
bootHypothesisClient(document, { assetRoot, manifest, sidebarAppUrl }); bootHypothesisClient(document, {
assetRoot,
manifest,
notebookAppUrl,
sidebarAppUrl,
});
} }
} else { } else {
// Show a "quiet" warning to avoid being disruptive on non-Hypothesis sites // Show a "quiet" warning to avoid being disruptive on non-Hypothesis sites
......
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