-
Notifications
You must be signed in to change notification settings - Fork 1
/
dereferencing.js
140 lines (109 loc) · 3.75 KB
/
dereferencing.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Highlight the quote when one is found in the URL's fragment identifier.
var Tracker = require('trackr');
var ReactiveVar = require('trackr-reactive-var');
var TextQuoteAnchor = require('dom-anchor-text-quote');
var highlightRange = require('dom-highlight-range');
var scrollIntoView = require('scroll-into-view');
var selectorInUrl = require('./oa-selector-in-url');
// Whether module is enabled.
var enabled = new ReactiveVar(false);
// Remembers how to remove the previously set highlight.
var cleanupHighlight = null;
function onHashChange(event) {
if (enabled.get()) {
var fragmentIdentifier = getFragmentIdentifier();
processFragmentIdentifier(fragmentIdentifier, {scroll: true});
}
}
function getFragmentIdentifier() {
// Read the #hash part from the URL
var hash = window.location.hash;
// Ditch the '#', decode URI-escaped characters
var fragmentIdentifier = window.decodeURIComponent(hash.substring(1, hash.length));
return fragmentIdentifier;
}
function processFragmentIdentifier(fragmentIdentifier, options) {
if (options === undefined) options = {};
var scroll = options.scroll;
// Remove the previous highlight, if any.
if (cleanupHighlight !== null) {
cleanupHighlight();
cleanupHighlight = null;
}
// If an OA-selector is found in the fragment identifier, highlight its target.
var selector = selectorInUrl.selectorFromFragmentIdentifier(fragmentIdentifier);
if (selector !== null) {
// The actual dereferencing in the document.
var range = rangeFromSelector(selector);
if (range === null) {
console.error("Could not locate the specified fragment: '" + fragmentIdentifier + "'.");
return;
}
// Highlight the range.
cleanupHighlight = highlightRange(range, 'highlighted-by-url');
// Scroll to the start of the range if desired.
if (scroll) {
var element = range.startContainer.parentElement;
scrollIntoView(element, {time: 200});
}
}
}
function rangeFromSelector(selector) {
// Try to find the target of the selector in the document.
var type = selector.type;
var range;
if (type == 'TextQuoteSelector') {
try {
range = TextQuoteAnchor.fromSelector(document.body, selector).toRange();
} catch (e) {
return null;
}
}
else {
throw new Error("Unsupported selector type: '"+type+"'.");
}
return range;
}
function runOnce(options) {
if (options === undefined) options = {};
var run = function () {
var fragmentIdentifier = getFragmentIdentifier();
processFragmentIdentifier(fragmentIdentifier, options);
};
// Run directly or as soon as possible.
if (['loaded', 'interactive', 'complete'].indexOf(document.readyState) > -1) {
run();
}
else {
// Run as soon as the DOM has loaded.
window.addEventListener("load", run);
}
}
Tracker.autorun(function () {
// Add and remove event listeners whenever we are switched on or off
if (enabled.get()) {
enable();
}
else {
if (!Tracker.currentComputation.firstRun) { // We naturally start disabled.
disable();
}
}
});
function enable() {
// Run every time the fragment identifier changes.
window.addEventListener("hashchange", onHashChange);
}
function disable() {
window.removeEventListener("hashchange", onHashChange);
window.removeEventListener("load", onHashChange);
// Clean up the current highlight, if any.
if (cleanupHighlight !== null) {
cleanupHighlight();
cleanupHighlight = null;
}
}
module.exports = {
enabled: enabled,
runOnce: runOnce,
};