Skip to content

Commit 23ca5e3

Browse files
committed
Added Element relative Highlighter Range.
Uses a DOM element as doc. This is more failsafe at HTML structure changes.
1 parent 1e55169 commit 23ca5e3

File tree

8 files changed

+213
-37
lines changed

8 files changed

+213
-37
lines changed

demos/highlighter_text_range.html

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<!DOCTYPE html>
2+
<!--[if lte IE 6]><html class="ie6"><!--[if gt IE 8]><!--><html><!--<![endif]-->
3+
<head>
4+
<title>Rangy Highlighter Module Demo</title>
5+
<link href="demo.css" rel="stylesheet" type="text/css">
6+
<style type="text/css">
7+
.highlight {
8+
background-color: yellow;
9+
}
10+
11+
.note {
12+
background-color: limegreen;
13+
}
14+
15+
#summary {
16+
border: dotted orange 1px;
17+
}
18+
</style>
19+
<script type="text/javascript" src="../lib/rangy-core.js"></script>
20+
<script type="text/javascript" src="../lib/rangy-textrange.js"></script>
21+
<script type="text/javascript" src="../lib/rangy-classapplier.js"></script>
22+
<script type="text/javascript" src="../lib/rangy-highlighter.js"></script>
23+
<script type="text/javascript">
24+
function gEBI(id) {
25+
return document.getElementById(id);
26+
}
27+
var serializedHighlights = decodeURIComponent(window.location.search.slice(window.location.search.indexOf("=") + 1));
28+
var highlighter;
29+
30+
var initialDoc;
31+
32+
window.onload = function() {
33+
rangy.init();
34+
35+
highlighter = rangy.createHighlighter(document.getElementById("content"), "TextRange");
36+
37+
highlighter.addClassApplier(rangy.createClassApplier("highlight", {
38+
ignoreWhiteSpace: true,
39+
tagNames: ["span", "a"]
40+
}));
41+
42+
highlighter.addClassApplier(rangy.createClassApplier("note", {
43+
ignoreWhiteSpace: true,
44+
elementTagName: "a",
45+
elementProperties: {
46+
href: "#",
47+
onclick: function() {
48+
var highlight = highlighter.getHighlightForElement(this);
49+
if (window.confirm("Delete this note (ID " + highlight.id + ")?")) {
50+
highlighter.removeHighlights( [highlight] );
51+
}
52+
return false;
53+
}
54+
}
55+
}));
56+
57+
58+
if (serializedHighlights) {
59+
highlighter.deserialize(serializedHighlights);
60+
}
61+
};
62+
63+
64+
function highlightSelectedText() {
65+
highlighter.highlightSelection("highlight");
66+
}
67+
68+
function noteSelectedText() {
69+
highlighter.highlightSelection("note");
70+
}
71+
72+
function removeHighlightFromSelectedText() {
73+
highlighter.unhighlightSelection();
74+
}
75+
76+
function highlightScopedSelectedText() {
77+
highlighter.highlightSelection("highlight", { containerElementId: "summary" });
78+
}
79+
80+
function noteScopedSelectedText() {
81+
highlighter.highlightSelection("note", { containerElementId: "summary" });
82+
}
83+
84+
function reloadPage(button) {
85+
button.form.elements["serializedHighlights"].value = highlighter.serialize();
86+
button.form.submit();
87+
}
88+
89+
</script>
90+
</head>
91+
<body>
92+
<div id="buttons">
93+
<h3>Highlighter</h3>
94+
<p>Make a selection in the document and use the buttons below to highlight and unhighlight.</p>
95+
<input type="button" ontouchstart="highlightSelectedText();" onclick="highlightSelectedText();" value="Highlight selection">
96+
<input type="button" ontouchstart="noteSelectedText();" onclick="noteSelectedText();" value="Add note to selection">
97+
<input type="button" ontouchstart="removeHighlightFromSelectedText();" onclick="removeHighlightFromSelectedText();" value="Remove highlights from selection">
98+
<br>
99+
<input type="button" ontouchstart="highlightScopedSelectedText();" onclick="highlightScopedSelectedText();" value="Highlight within outlined paragraph">
100+
<input type="button" ontouchstart="noteScopedSelectedText();" onclick="noteScopedSelectedText();" value="Annotate selection within outlined paragraph">
101+
102+
<h3>Preserving highlights between page requests</h3>
103+
<form action="highlighter_text_range.html" method="get">
104+
Highlights can be preserved between page requests. Press the following button to reload the page with the
105+
highlights preserved:
106+
<br>
107+
<input type="hidden" name="serializedHighlights" value="">
108+
<input type="button" value="Reload" onclick="reloadPage(this)">
109+
</form>
110+
</div>
111+
112+
<div id="content">
113+
<h1>Rangy Highlighter Module Demo</h1>
114+
<p id="intro">
115+
Please use your mouse and/or keyboard to make selections from the sample content below and use the buttons
116+
on the left hand size to create and remove highlights.
117+
</p>
118+
<p id="summary">
119+
<b>Association football</b> is a sport played between two teams. It is usually called <b>football</b>, but in
120+
some countries, such as the United States, it is called <b>soccer</b>. In
121+
<a href="http://simple.wikipedia.org/wiki/Japan">Japan</a>, New Zealand, South Africa, Australia, Canada and
122+
Republic of Ireland, both words are commonly used.
123+
</p>
124+
<p>
125+
Each team has 11 players on the field. One of these players is the <i>goalkeeper</i>, and the other ten are
126+
known as <i>"outfield players."</i> The game is played by <b>kicking a ball into the opponent's goal</b>. A
127+
match has 90 minutes of play, with a break of 15 minutes in the middle. The break in the middle is called
128+
half-time.
129+
</p>
130+
<h2>Competitions</h2>
131+
<p>
132+
There are many competitions for football, for both football clubs and countries. Football clubs usually play
133+
other teams in their own country, with a few exceptions. <b>Cardiff City F.C.</b> from Wales for example, play
134+
in the English leagues and in the English FA Cup.
135+
</p>
136+
<h2>Who plays football <span class="smaller">(this section is in pre-formatted text)</span></h2>
137+
<pre>
138+
Football is the world's most popular sport. It is played in more
139+
countries than any other game. In fact, FIFA (the Federation
140+
Internationale de Football Association) has more members than the
141+
United Nations.
142+
143+
It is played by both males and females.
144+
145+
146+
</pre>
147+
</div>
148+
149+
<p class="small">
150+
Text adapted from <a href="http://simple.wikipedia.org/wiki/Association_football">Simple Wikipedia page on
151+
Association Football</a>, licensed under the
152+
<a href="http://simple.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License">Creative
153+
Commons Attribution/Share-Alike License</a>.
154+
</p>
155+
</body>
156+
</html>

lib/rangy-classapplier.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
*
88
* Depends on Rangy core.
99
*
10-
* Copyright 2015, Tim Down
10+
* Copyright 2016, Tim Down
1111
* Licensed under the MIT license.
1212
* Version: 1.3.1-dev
13-
* Build date: 20 May 2015
13+
* Build date: 15 January 2016
1414
*/
1515
(function(factory, root) {
1616
if (typeof define == "function" && define.amd) {
@@ -698,13 +698,10 @@
698698
// Normalizes nodes after applying a class to a Range.
699699
postApply: function(textNodes, range, positionsToPreserve, isUndo) {
700700
var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
701-
702701
var merges = [], currentMerge;
703-
704702
var rangeStartNode = firstNode, rangeEndNode = lastNode;
705703
var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
706-
707-
var textNode, precedingTextNode;
704+
var precedingTextNode;
708705

709706
// Check for every required merge and create a Merge object for each
710707
forEach(textNodes, function(textNode) {
@@ -741,7 +738,7 @@
741738

742739
// Apply the merges
743740
if (merges.length) {
744-
for (i = 0, len = merges.length; i < len; ++i) {
741+
for (var i = 0, len = merges.length; i < len; ++i) {
745742
merges[i].doMerge(positionsToPreserve);
746743
}
747744

lib/rangy-core.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* Rangy, a cross-browser JavaScript range and selection library
33
* https://github.com/timdown/rangy
44
*
5-
* Copyright 2015, Tim Down
5+
* Copyright 2016, Tim Down
66
* Licensed under the MIT license.
77
* Version: 1.3.1-dev
8-
* Build date: 20 May 2015
8+
* Build date: 15 January 2016
99
*/
1010

1111
(function(factory, root) {
@@ -222,7 +222,7 @@
222222
})();
223223

224224
// Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
225-
// normalization of event properties
225+
// normalization of event properties because we don't need this.
226226
var addListener;
227227
if (isBrowser) {
228228
if (isHostMethod(document, "addEventListener")) {
@@ -1303,6 +1303,7 @@
13031303
var getDocumentOrFragmentContainer = createAncestorFinder( [9, 11] );
13041304
var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
13051305
var getDocTypeNotationEntityAncestor = createAncestorFinder( [6, 10, 12] );
1306+
var getElementAncestor = createAncestorFinder( [1] );
13061307

13071308
function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
13081309
if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
@@ -1365,7 +1366,7 @@
13651366
var htmlParsingConforms = false;
13661367
try {
13671368
styleEl.innerHTML = "<b>x</b>";
1368-
htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
1369+
htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Pre-Blink Opera incorrectly creates an element node
13691370
} catch (e) {
13701371
// IE 6 and 7 throw
13711372
}
@@ -1966,6 +1967,12 @@
19661967
break;
19671968
}
19681969

1970+
assertNoDocTypeNotationEntityAncestor(sc, true);
1971+
assertValidOffset(sc, so);
1972+
1973+
assertNoDocTypeNotationEntityAncestor(ec, true);
1974+
assertValidOffset(ec, eo);
1975+
19691976
boundaryUpdater(this, sc, so, ec, eo);
19701977
},
19711978

@@ -2128,6 +2135,12 @@
21282135
assertNoDocTypeNotationEntityAncestor(node, true);
21292136
assertValidOffset(node, offset);
21302137
this.setStartAndEnd(node, offset);
2138+
},
2139+
2140+
parentElement: function() {
2141+
assertRangeValid(this);
2142+
var parentNode = this.commonAncestorContainer;
2143+
return parentNode ? getElementAncestor(this.commonAncestorContainer, true) : null;
21312144
}
21322145
});
21332146

@@ -2149,17 +2162,11 @@
21492162
range.endContainer = endContainer;
21502163
range.endOffset = endOffset;
21512164
range.document = dom.getDocument(startContainer);
2152-
21532165
updateCollapsedAndCommonAncestor(range);
21542166
}
21552167

21562168
function Range(doc) {
2157-
this.startContainer = doc;
2158-
this.startOffset = 0;
2159-
this.endContainer = doc;
2160-
this.endOffset = 0;
2161-
this.document = doc;
2162-
updateCollapsedAndCommonAncestor(this);
2169+
updateBoundaries(this, doc, 0, doc, 0);
21632170
}
21642171

21652172
createPrototypeRange(Range, updateBoundaries);

lib/rangy-highlighter.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
*
55
* Depends on Rangy core, ClassApplier and optionally TextRange modules.
66
*
7-
* Copyright 2015, Tim Down
7+
* Copyright 2016, Tim Down
88
* Licensed under the MIT license.
99
* Version: 1.3.1-dev
10-
* Build date: 20 May 2015
10+
* Build date: 15 January 2016
1111
*/
1212
(function(factory, root) {
1313
if (typeof define == "function" && define.amd) {
@@ -34,8 +34,16 @@
3434
return h1.characterRange.start - h2.characterRange.start;
3535
}
3636

37+
function getDocumentFromUnknownElement(doc){
38+
return Object.prototype.toString.call(doc) == "[object HTMLDocument]" ? doc : doc.ownerDocument;
39+
}
40+
41+
function getContainerElementId(doc){
42+
return Object.prototype.toString.call(doc) != "[object HTMLDocument]" ? doc.getAttribute('id') : null;
43+
}
44+
3745
function getContainerElement(doc, id) {
38-
return id ? doc.getElementById(id) : getBody(doc);
46+
return id ? getDocumentFromUnknownElement(doc).getElementById(id) : getBody(doc);
3947
}
4048

4149
/*----------------------------------------------------------------------------------------------------------------*/
@@ -330,7 +338,7 @@
330338
var classApplier = className ? this.classAppliers[className] : null;
331339

332340
options = createOptions(options, {
333-
containerElementId: null,
341+
containerElementId: getContainerElementId(this.doc),
334342
exclusive: true
335343
});
336344

@@ -339,7 +347,7 @@
339347

340348
var containerElement, containerElementRange, containerElementCharRange;
341349
if (containerElementId) {
342-
containerElement = this.doc.getElementById(containerElementId);
350+
containerElement = getDocumentFromUnknownElement(this.doc).getElementById(containerElementId);
343351
if (containerElement) {
344352
containerElementRange = api.createRange(this.doc);
345353
containerElementRange.selectNodeContents(containerElement);
@@ -431,7 +439,7 @@
431439
var converter = this.converter;
432440

433441
options = createOptions(options, {
434-
containerElement: null,
442+
containerElement: this.doc,
435443
exclusive: true
436444
});
437445

@@ -459,7 +467,7 @@
459467
var classApplier = className ? this.classAppliers[className] : false;
460468

461469
options = createOptions(options, {
462-
containerElementId: null,
470+
containerElementId: getContainerElementId(this.doc),
463471
exclusive: true
464472
});
465473

lib/rangy-selectionsaverestore.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
*
88
* Depends on Rangy core.
99
*
10-
* Copyright 2015, Tim Down
10+
* Copyright 2016, Tim Down
1111
* Licensed under the MIT license.
1212
* Version: 1.3.1-dev
13-
* Build date: 20 May 2015
13+
* Build date: 15 January 2016
1414
*/
1515
(function(factory, root) {
1616
if (typeof define == "function" && define.amd) {
@@ -24,7 +24,7 @@
2424
factory(root.rangy);
2525
}
2626
})(function(rangy) {
27-
rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
27+
rangy.createModule("SaveRestore", ["WrappedSelection"], function(api, module) {
2828
var dom = api.dom;
2929
var removeNode = dom.removeNode;
3030
var isDirectionBackward = api.Selection.isDirectionBackward;

lib/rangy-serializer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
*
99
* Depends on Rangy core.
1010
*
11-
* Copyright 2015, Tim Down
11+
* Copyright 2016, Tim Down
1212
* Licensed under the MIT license.
1313
* Version: 1.3.1-dev
14-
* Build date: 20 May 2015
14+
* Build date: 15 January 2016
1515
*/
1616
(function(factory, root) {
1717
if (typeof define == "function" && define.amd) {

lib/rangy-textrange.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*
2525
* Depends on Rangy core.
2626
*
27-
* Copyright 2015, Tim Down
27+
* Copyright 2016, Tim Down
2828
* Licensed under the MIT license.
2929
* Version: 1.3.1-dev
30-
* Build date: 20 May 2015
30+
* Build date: 15 January 2016
3131
*/
3232

3333
/**

0 commit comments

Comments
 (0)