Commit 25b2516d authored by Robert Knight's avatar Robert Knight

Make '#annotations' fragment parsing more robust

When parsing the '#annotations' fragment, ignore any extra content
outside of the url-safe-base64 character set for annotation IDs.

This helps in cases where extra content has been appended to the URL
fragment after '#annotations:<ID>', before the client loads. This
happens at one point during loading of Medium.com pages for example.
parent 82d1ea97
'use strict'; 'use strict';
var annotationIDs = require('../util/annotation-ids');
var docs = 'https://h.readthedocs.org/en/latest/hacking/customized-embedding.html'; var docs = 'https://h.readthedocs.org/en/latest/hacking/customized-embedding.html';
/** /**
...@@ -13,6 +15,18 @@ function config(window_) { ...@@ -13,6 +15,18 @@ function config(window_) {
document.querySelector('link[type="application/annotator+html"]').href, document.querySelector('link[type="application/annotator+html"]').href,
}; };
// Parse config from `<meta name="hypothesis-config" content="<JSON>">` tags
var configElement = window_.document
.querySelector('meta[name="hypothesis-config"]');
if (configElement) {
try {
Object.assign(options, JSON.parse(configElement.content));
} catch (err) {
console.warn('Could not parse Hypothesis config from', configElement);
}
}
// Parse config from `window.hypothesisConfig` function
if (window_.hasOwnProperty('hypothesisConfig')) { if (window_.hasOwnProperty('hypothesisConfig')) {
if (typeof window_.hypothesisConfig === 'function') { if (typeof window_.hypothesisConfig === 'function') {
Object.assign(options, window_.hypothesisConfig()); Object.assign(options, window_.hypothesisConfig());
...@@ -21,9 +35,9 @@ function config(window_) { ...@@ -21,9 +35,9 @@ function config(window_) {
} }
} }
var annotFragmentMatch = window_.location.hash.match(/^#annotations:(.*)/); var directLinkedID = annotationIDs.extractIDFromURL(window_.location.href);
if (annotFragmentMatch) { if (directLinkedID) {
options.annotations = annotFragmentMatch[1]; options.annotations = directLinkedID;
} }
return options; return options;
} }
......
...@@ -3,13 +3,28 @@ ...@@ -3,13 +3,28 @@
var config = require('../config'); var config = require('../config');
describe('annotator configuration', function () { describe('annotator configuration', function () {
var fakeMetaConfig;
var fakeWindowBase = { var fakeWindowBase = {
document: { document: {
querySelector: sinon.stub().returns({href: 'app.html'}), querySelector: sinon.spy(function (selector) {
if (selector === 'link[type="application/annotator+html"]') {
return {href: 'app.html'};
} else if (selector === 'meta[name="hypothesis-config"]' &&
fakeMetaConfig) {
return {content:fakeMetaConfig};
} else {
return null;
}
}),
}, },
location: {hash: ''}, location: {hash: ''},
}; };
beforeEach(function () {
fakeMetaConfig = '';
});
it('reads the app src from the link tag', function () { it('reads the app src from the link tag', function () {
var linkEl = document.createElement('link'); var linkEl = document.createElement('link');
linkEl.type = 'application/annotator+html'; linkEl.type = 'application/annotator+html';
...@@ -23,7 +38,7 @@ describe('annotator configuration', function () { ...@@ -23,7 +38,7 @@ describe('annotator configuration', function () {
it('reads the #annotation query fragment', function () { it('reads the #annotation query fragment', function () {
var fakeWindow = Object.assign({}, fakeWindowBase, { var fakeWindow = Object.assign({}, fakeWindowBase, {
location: {hash:'#annotations:456'}, location: {href:'https://foo.com/#annotations:456'},
}); });
assert.deepEqual(config(fakeWindow), { assert.deepEqual(config(fakeWindow), {
app: 'app.html', app: 'app.html',
...@@ -42,4 +57,12 @@ describe('annotator configuration', function () { ...@@ -42,4 +57,12 @@ describe('annotator configuration', function () {
firstRun: true, firstRun: true,
}); });
}); });
it('merges the config from the "hypothesis-config" meta tag', function () {
fakeMetaConfig = '{"annotations":"456"}';
assert.deepEqual(config(fakeWindowBase), {
app: 'app.html',
annotations: '456',
});
});
}); });
'use strict';
function extractIDFromURL(url) {
try {
var annotFragmentMatch = url.match(/#annotations:([A-Za-z0-9_-]+)$/);
if (annotFragmentMatch) {
return annotFragmentMatch[1];
} else {
return null;
}
} catch (err) {
return null;
}
}
module.exports = {
extractIDFromURL: extractIDFromURL,
};
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