23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Tim Down
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/test/highlightertests.js:
--------------------------------------------------------------------------------
1 | xn.test.suite("Highlighter module tests", function(s) {
2 | s.tearDown = function() {
3 | document.getElementById("test").innerHTML = "";
4 | };
5 |
6 | s.test("highlightSelection test", function(t) {
7 | var applier = rangy.createClassApplier("c1");
8 | var highlighter = rangy.createHighlighter();
9 | highlighter.addClassApplier(applier);
10 |
11 | var testEl = document.getElementById("test");
12 | var range = rangyTestUtils.createRangeInHtml(testEl, 'one [two] three four');
13 | range.select();
14 |
15 | var highlights = highlighter.highlightSelection("c1");
16 |
17 | t.assertEquals(highlights.length, 1);
18 |
19 |
20 | //t.assertEquals(highlights.length, 1);
21 |
22 |
23 | });
24 |
25 | s.test("Options test (issue 249)", function(t) {
26 | var applier = rangy.createClassApplier("c1");
27 | var highlighter = rangy.createHighlighter();
28 | highlighter.addClassApplier(applier);
29 |
30 | highlighter.highlightSelection("c1", { selection: rangy.getSelection() });
31 | });
32 |
33 | }, false);
34 |
--------------------------------------------------------------------------------
/test/textareatests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rangy - Tests
6 |
7 |
8 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/fiddlings/218.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
This is my text1 which has a 345678 footnote
20 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Rangy
3 |
4 | ## Core
5 |
6 | The following are the core Rangy objects (try a live demo [here](https://lezuse.github.io/rangy/demos/core.html)):
7 |
8 | * [[The main Rangy object|Rangy Object]], which acts as a namespace for Rangy functionality;
9 | * [[Range|Rangy Range]], representing a portion of an HTML document and based on the DOM Level 2 Range interface;
10 | * [[Selection|Rangy Selection]], representing the user's selection within an HTML document.
11 | * [[DOM Utils|Dom Utils]], a collection of DOM-related helper functions used internally by Rangy but also available via `rangy.dom`.
12 |
13 | ## Modules
14 |
15 | Rangy also comes with optional modules, each of which comes in a separate .js file. Current modules are:
16 |
17 | * [[Selection save and restore|Selection Save Restore Module]]
18 | * [[Class apply and remove to/from selection|Class Applier Module]]
19 | * [[Selection and Range serialization|Serializer Module]]
20 | * [[Text-based extensions to selection and range|Text Range Module]]
21 | * [[Highlighter module providing an API for creating, serializing and deserializing text highlights|Highlighter Module]]
22 |
23 | ## Other documentation
24 |
25 | Other pages within the documentation are at various stages of completion and will be linked from this page when completed.
--------------------------------------------------------------------------------
/test/html5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rangy - HTML5 Tests
6 |
7 |
8 |
15 |
16 |
17 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test/ietextnodes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
25 |
26 |
27 |
28 | One
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Rangy Updated
2 | =============
3 | This is a clone of Tim Down's original, and awesome range and selection JavaScript library, [Rangy](https://github.com/timdown/rangy). The original project has been abandoned for 3 years (at the time of this writing) and there are no alternative libraries that match the functionality and ease-of-use of the original Rangy.
4 |
5 | This project is a continuation of the original Rangy project. It is starting off as a direct clone of the original Rangy and will migrate from there.
6 |
7 | We definitely accept pull requests, and are looking for additional contributors to the project.
8 |
9 | ## Rangy
10 |
11 | A cross-browser JavaScript range and selection library.
12 |
13 | The current version is version 1.3.0.
14 |
15 | The latest source code and releases are on [GitHub](../../releases).
16 |
17 | ## Getting Started
18 |
19 | ```
20 | npm i -S rangy-updated
21 | ```
22 |
23 | ## Bower
24 |
25 | There is now an official Rangy package for Bower with Rangy 1.2 and 1.3 versions, called `rangy`.
26 |
27 | ## AMD
28 |
29 | Rangy 1.3 has AMD support.
30 |
31 | ## NPM
32 |
33 | There is an official Rangy module on NPM called [`rangy`](https://www.npmjs.org/package/rangy).
34 |
35 | ## Documentation
36 |
37 | Documentation is in [the docs folder](https://github.com/pburrows/rangy-updated/blob/master/docs/).
38 |
39 |
40 | ## Build
41 |
42 | run `node .\builder\build.js` locally
--------------------------------------------------------------------------------
/demos/demo.css:
--------------------------------------------------------------------------------
1 | #intro {
2 | font-size: 125%;
3 | color: green;
4 | }
5 |
6 | p.small {
7 | font-size: 75%;
8 | }
9 |
10 | span.smaller {
11 | font-size: 75%;
12 | }
13 |
14 | pre {
15 | padding: 3px;
16 | }
17 | #content {
18 | margin-left: 330px;
19 | }
20 |
21 | #buttons {
22 | left: 10px;
23 | position: fixed;
24 | border: solid black 1px;
25 | background-color: lightgoldenrodyellow;
26 | padding: 5px;
27 | width: 300px;
28 | }
29 |
30 | html.ie6 #buttons {
31 | position: absolute;
32 | }
33 |
34 | #buttons h3 {
35 | font-size: 125%;
36 | font-weight: bold;
37 | margin: 0;
38 | padding: 0.25em 0;
39 | }
40 |
41 | #buttons h4 {
42 | font-size: 100%;
43 | font-weight: normal;
44 | font-style: italic;
45 | margin: 0;
46 | padding: 0.25em 0;
47 | }
48 |
49 | #code {
50 | width: 95%;
51 | }
52 |
53 | #selectioncontent {
54 | border: solid black 1px;
55 | padding: 0.125em;
56 | background-color: white;
57 | overflow: auto;
58 | }
59 |
60 | *.unselectable {
61 | -moz-user-select: -moz-none;
62 | -khtml-user-select: none;
63 | -webkit-user-select: none;
64 |
65 | /*
66 | Introduced in IE 10.
67 | See http://ie.microsoft.com/testdrive/HTML5/msUserSelect/
68 | */
69 | -ms-user-select: none;
70 | user-select: none;
71 | }
72 |
73 | *.warning {
74 | border: solid darkred 2px;
75 | padding: 3px;
76 | background-color: #f77;
77 | }
78 |
--------------------------------------------------------------------------------
/test/ie9controlrange.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
27 |
28 |
29 | and
30 |
31 |
32 |
--------------------------------------------------------------------------------
/test/bold.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
32 |
37 |
38 |
39 |
Some lovely bold content and strong stuff and
40 | a styled span, yeah, and a classy span
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/test/featuretests.js:
--------------------------------------------------------------------------------
1 | xn.test.suite("Browser feature tests", function(s) {
2 | rangy.init();
3 |
4 | // Detect browser version roughly. It doesn't matter too much: these are only rough tests designed to test whether
5 | // Rangy's feature detection is hopelessly wrong
6 |
7 |
8 | var browser = jQuery.browser;
9 | var isIe = !!browser.msie;
10 | var isMozilla = !!browser.mozilla;
11 | var isOpera = !!browser.opera;
12 | var version = parseFloat(browser.version);
13 |
14 | s.test("DOM Range support", function(t) {
15 | t.assertEquals(rangy.features.implementsDomRange, !isIe || version >= 9);
16 | });
17 |
18 | s.test("TextRange support", function(t) {
19 | t.assertEquals(rangy.features.implementsTextRange, isIe && version >= 4);
20 | });
21 |
22 | s.test("document.selection support", function(t) {
23 | t.assertEquals(rangy.features.implementsTextRange, isIe && version >= 4);
24 | });
25 |
26 | s.test("window.getSelection() support", function(t) {
27 | t.assertEquals(rangy.features.implementsWinGetSelection, !isIe || version >= 9);
28 | });
29 |
30 | s.test("selection has rangeCount", function(t) {
31 | t.assertEquals(rangy.features.selectionHasRangeCount, !isIe || version >= 9);
32 | });
33 |
34 | s.test("selection has anchor and focus support", function(t) {
35 | t.assertEquals(rangy.features.selectionHasAnchorAndFocus, !isIe || version >= 9);
36 | });
37 |
38 | s.test("selection has extend() method", function(t) {
39 | t.assertEquals(rangy.features.selectionHasExtend, !isIe);
40 | });
41 |
42 | s.test("HTML parsing", function(t) {
43 | t.assertEquals(rangy.features.htmlParsingConforms, !isIe);
44 | });
45 |
46 | s.test("Multiple ranges per selection support", function(t) {
47 | t.assertEquals(rangy.features.selectionSupportsMultipleRanges, isMozilla);
48 | });
49 |
50 | s.test("Collapsed non-editable selections support", function(t) {
51 | t.assertEquals(rangy.features.collapsedNonEditableSelectionsSupported, !isOpera);
52 | });
53 | }, false);
54 |
--------------------------------------------------------------------------------
/test/textrangeperformancetests.js:
--------------------------------------------------------------------------------
1 | rangy.config.preferTextRange = true;
2 |
3 | xn.test.suite("Range miscellaneous", function(s) {
4 | rangy.init();
5 |
6 | var elementCount = 1000;
7 | var testCount = 20;
8 |
9 | function setUp(t) {
10 | t.testEl = document.createElement("div");
11 | t.testEl.innerHTML = new Array(elementCount + 1).join("One twothree four");
12 | document.body.appendChild(t.testEl);
13 | var textRange = document.body.createTextRange();
14 | textRange.moveToElementText(t.testEl);
15 | var textLength = textRange.text.length;
16 |
17 | t.textRanges = [];
18 |
19 | for (var i = 0, start, end; i < testCount; ++i) {
20 | textRange = document.body.createTextRange();
21 | textRange.moveToElementText(t.testEl);
22 |
23 | start = Math.floor(textLength * Math.random());
24 | end = start + Math.floor((textLength - start) * Math.random());
25 |
26 | textRange.collapse(true);
27 | textRange.moveEnd("character", end);
28 | textRange.moveStart("character", start);
29 |
30 | if (Math.random() < 0.3) {
31 | textRange.collapse(true);
32 | }
33 | t.textRanges[i] = textRange;
34 | }
35 | }
36 |
37 | function tearDown(t) {
38 | t.testEl.parentNode.removeChild(t.testEl);
39 | }
40 |
41 | if (document.body.createTextRange) {
42 | s.test("TextRange to Range control", function(t) {
43 | //t.assertEquals(t.testEl.childNodes.length, 2 * elementCount);
44 | for (var i = 0, len = t.textRanges.length, range; i < len; ++i) {
45 | t.textRanges[i].select();
46 | }
47 | }, setUp, tearDown);
48 |
49 | s.test("TextRange to Range speed test (binary search)", function(t) {
50 | rangy.init();
51 | for (var i = 0, len = t.textRanges.length, sel; i < len; ++i) {
52 | t.textRanges[i].select();
53 | sel = rangy.getSelection();
54 | t.assertEquals(t.textRanges[i].text, sel.toString());
55 | }
56 | }, setUp, tearDown);
57 | }
58 | }, false);
59 |
--------------------------------------------------------------------------------
/docs/How-Rangy-Differs-From-Native-Apis.md:
--------------------------------------------------------------------------------
1 |
2 | # How the Rangy Range and Selection APIs differ from native browser APIs
3 |
4 | Rangy implements the [DOM Level 2 Range specification](http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html), the draft WHATWG Selection specification (formerly part of HTML5) and existing browser behaviour as closely as possible. However, while you can usually use exactly the same code with Rangy ranges and selections as you could with with the native APIs of standards-based browsers, there are a few key differences that prevent this always being the case. This page lists these differences.
5 |
6 | ## Ranges
7 |
8 | ### Range objects do not update when the DOM is changed, except when it is a range method doing the changing
9 |
10 | This is the major stumbling block. The DOM4 Range specification does specify what should happen to a Range under DOM mutation and browsers do implement it. However, without [DOM mutation events](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents) or [DOM mutation observers](https://dom.spec.whatwg.org/#mutation-observers) (both of which are absent in IE <= 8), it's simply impossible to detect all DOM mutation and therefore impossible for a Rangy range to update itself in response to DOM mutation. Therefore since the main goal of Rangy is to provide the same functionality in all supported browsers, Rangy makes no attempt to follow this aspect of the specification.
11 |
12 | ### None of the properties of a Range object throw an exception when assigned to
13 |
14 | Simple solution: do not attempt to assign a value to a Rangy range property (`startContainer`, `endContainer`, `startOffset`, `endOffset`, `commonAncestorContainer` and `collapsed`), since it's not legal for native ranges anyway.
15 |
16 | ### Properties of a detached Range object throw no exceptions when accessed
17 |
18 | This is also simply solved: do not attempt to interact with a detached Range.
19 |
20 |
21 | ## Selections
22 |
23 | Rangy's `Selection` object is missing `modify()`, `setBaseAndExtent()` and `extend()` methods. This is because for `modify()`, the use case is better supplied by the [[TextRange module|Text Range Module]], and for other two methods, it is not possible to implement these in Internet Explorer up to and including version 11.
--------------------------------------------------------------------------------
/test/controlrange2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
39 |
40 |
41 |
42 | Pictures of Steve Claridge:
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/roadmap.txt:
--------------------------------------------------------------------------------
1 | 1.3
2 | ---
3 |
4 | - [X] TextRange module
5 | (http://groups.google.com/group/rangy/browse_frm/thread/bd7a351e63a16474)
6 | - [X] Allow Window, Document, iframe and DOM node as params to range/selection creation methods
7 | - [X] Add rangy.features.implementsWinGetSelection and rangy.features.implementsDocSelection
8 | - [X] Check Range and Selection against WHATWG Range spec algorithms
9 | - [X] Highlighter module. Review and rewrite existing.
10 | - [X] Added select() method to range
11 | - [X] Rename CSS class applier module to "class applier module"
12 | - [X] Add handling for img and similar elements in class applier module
13 | - [X] AMD support
14 | - [X] Add getNativeTextRange() to selection for IE 11
15 | - [X] Add Range setStartAndEnd(). Overloaded? eg. two args collapsed, three args (node, startOffset, endOffset),
16 | four args (startNode, startOffset, endNode, endOffset) (consider this)
17 |
18 | 1.4
19 | ---
20 |
21 | - [?] Consider range.restrict(node)
22 | - [?] Consider filter option in createClassApplier() options object
23 | - [ ] Either a utils module or an FAQ page with code snippets for common use cases, including:
24 | - [X] Simple selection save/restore (bookmark?) (is this necessary?)
25 | - [ ] Insert HTML
26 | (http://stackoverflow.com/questions/2213376/how-to-find-cursor-position-in-a-contenteditable-div/2213514#2213514)
27 | - [?] Some kind of jQuery integration module?
28 | - [ ] Move IE <= 8 support into a separate optional module
29 | - [ ] Add withinRange and withinNode options to move(), moveStart() and moveEnd() methods
30 | - [?] Positions module
31 | (http://stackoverflow.com/questions/4122315/how-to-find-xy-position-in-javascript-with-offset/4123495#4123495)
32 | - [ ] Config module or something so that config can work with AMD. See PR #285
33 | (https://github.com/timdown/rangy/pull/285)
34 | - [ ] Move to one of the common testing libraries
35 | - [ ] Update build not to use a fresh Git checkout
36 | - [ ] Investigate shadow DOM (issue #307)
37 |
38 | Possible features for some version
39 | ----------------------------------
40 |
41 | - [?] Commands module with basic inline commands (bold, italic, colour, font face, font size, background colour, etc.)
42 | (http://stackoverflow.com/questions/2887101/apply-style-to-range-of-text-with-javascript-in-uiwebview/2888969#2888969)
43 | - [?] More commands (block? Insert line break? Think about this, don't want to build a WYSIWYG editor)
44 | - [ ] Add selection extend()? Don't think this is possible.
45 | - [ ] Option in TextRange module for alternative ways to extract text for an element (see email from Bruce Augustine)
46 |
--------------------------------------------------------------------------------
/demos/content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rangy Bold Demo
5 |
18 |
26 |
27 |
28 |
Test
29 |
Test
30 |
Test
31 |
32 |
2
33 |
One
34 | Two
35 |
Three
36 |
37 |
38 | Association football is a sport played between two teams. It is usually called football, but in
39 | some countries, such as the United States, it is called soccer. In
40 | Japan, New Zealand, South Africa, Australia, Canada and
41 | Republic of Ireland, both words are commonly used.
42 |
43 |
44 | Each team has 11 players on the field. One of these players is the goalkeeper, and the other ten are
45 | known as "outfield players." The game is played by kicking a ball into the opponent's goal. A
46 | match has 90 minutes of play, with a break of 15 minutes in the middle. The break in the middle is called
47 | half-time.
48 |
49 |
Competitions
50 |
51 | There are many competitions for football, for both football clubs and countries. Football clubs usually play
52 | other teams in their own country, with a few exceptions. Cardiff City F.C. from Wales for example, play
53 | in the English leagues and in the English FA Cup.
54 |
55 |
Who plays football
56 |
57 | Football is the world's most popular sport. It is played in more
58 | countries than any other game. In fact, FIFA (the Federation
59 | Internationale de Football Association) has more members than the
60 | United Nations.
61 |
62 | It is played by both males and females.
63 |
64 |
65 |
62 | 29 min: After that let-off, Portugal tear down the other end and sow panic in the Brazilian defence.
63 |
64 |
66 |
67 |
68 |
69 |
70 |
71 |
Zero
72 |
One two
73 |
Four five
74 |
29 min: After that let-off, Portugal tear down the other end and sow panic in the Brazilian defence, Juan forced into a clusmy-looking tackle on Tiago, who tumbles theatrically. Portugal players howl for a penalty, the ref books Tiago for diving.
75 |
28 min: Portugal switch off defensively, allowing Nilmar to collect a through-ball and shoot from eight yards. The keeper pushes it on to the psot and out! Great save. "Klose ..." blurts David Roberts. "... but no cigar."
76 |
77 | Some preformatted text.
78 |
79 | Wonder how it'll do
80 |
81 | with this, plus some line breaks
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/modules/rangy-util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utilities module for Rangy.
3 | * A collection of common selection and range-related tasks, using Rangy.
4 | *
5 | * Part of Rangy, a cross-browser JavaScript range and selection library
6 | * https://github.com/timdown/rangy
7 | *
8 | * Depends on Rangy core.
9 | *
10 | * Copyright %%build:year%%, Tim Down
11 | * Licensed under the MIT license.
12 | * Version: %%build:version%%
13 | * Build date: %%build:date%%
14 | */
15 | /* build:modularizeWithRangyDependency */
16 | rangy.createModule("Util", ["WrappedSelection"], function(api, module) {
17 | var rangeProto = api.rangePrototype;
18 | var selProto = api.selectionPrototype;
19 |
20 | selProto.pasteText = function(text) {
21 | this.deleteFromDocument();
22 | var range = this.getRangeAt(0);
23 | var textNode = range.getDocument().createTextNode(text);
24 | range.insertNode(textNode);
25 | this.setSingleRange(range);
26 | };
27 |
28 | rangeProto.pasteText = function(text) {
29 | this.deleteContents();
30 | var textNode = this.getDocument().createTextNode(text);
31 | this.insertNode(textNode);
32 | };
33 |
34 | selProto.pasteHtml = function(html) {
35 | this.deleteFromDocument();
36 | var range = this.getRangeAt(0);
37 | var frag = this.createContextualFragment(html);
38 | var lastNode = frag.lastChild;
39 | range.insertNode(frag);
40 | if (lastNode) {
41 | range.setStartAfter(lastNode)
42 | }
43 | this.setSingleRange(range);
44 | };
45 |
46 | rangeProto.pasteHtml = function(html) {
47 | this.deleteContents();
48 | var frag = this.createContextualFragment(html);
49 | this.insertNode(frag);
50 | };
51 |
52 | selProto.selectNodeContents = function(node) {
53 | var range = api.createRange(this.win);
54 | range.selectNodeContents(node);
55 | this.setSingleRange(range);
56 | };
57 |
58 | api.createRangeFromNode = function(node) {
59 | var range = api.createRange(node);
60 | range.selectNode(node);
61 | return range;
62 | };
63 |
64 | api.createRangeFromNodeContents = function(node) {
65 | var range = api.createRange(node);
66 | range.selectNodeContents(node);
67 | return range;
68 | };
69 |
70 | api.selectNodeContents = function(node) {
71 | api.getSelection().selectNodeContents(node);
72 | };
73 |
74 | rangeProto.selectSelectedTextElements = (function() {
75 | function isInlineElement(node) {
76 | return node.nodeType == 1 && api.dom.getComputedStyleProperty(node, "display") == "inline";
77 | }
78 |
79 | function getOutermostNodeContainingText(range, node) {
80 | var outerNode = null;
81 | var nodeRange = range.cloneRange();
82 | nodeRange.selectNode(node);
83 | if (nodeRange.toString() !== "") {
84 | while ( (node = node.parentNode) && isInlineElement(node) && range.containsNodeText(node) ) {
85 | outerNode = node;
86 | }
87 | }
88 | return outerNode;
89 | }
90 |
91 | return function() {
92 | var startNode = getOutermostNodeContainingText(this, this.startContainer);
93 | if (startNode) {
94 | this.setStartBefore(startNode);
95 | }
96 |
97 | var endNode = getOutermostNodeContainingText(this, this.endContainer);
98 | if (endNode) {
99 | this.setEndAfter(endNode);
100 | }
101 | };
102 | })();
103 |
104 | // TODO: simple selection save/restore
105 | });
106 | /* build:modularizeEnd */
--------------------------------------------------------------------------------
/docs/New-Features-In-One-Point-Three.md:
--------------------------------------------------------------------------------
1 |
2 | [[Documentation home|Home]]
3 |
4 | # New features in Rangy 1.3
5 |
6 | ### New TextRange module
7 |
8 | [Demo page](http://rangy.googlecode.com/svn/trunk/demos/textrange.html)
9 |
10 | Character and word-based Range and Selection manipulation, relative to the visible text on the page. This is a major new module with some powerful features:
11 |
12 | * `moveStart()`, `moveEnd()`, `move()` and `expand()` Range methods inspired by IE's `TextRange` object
13 | * Page text search, using a string or a regular expression
14 | * Character offset-based selection save and restore, immune to changes in `innerHTML`
15 | * Selection snap to words
16 | * Cross-browser `innerText` implementation
17 | * Selection and range trimming
18 |
19 | ### New Highlighter module
20 |
21 | [Demo page](http://rangy.googlecode.com/svn/trunk/demos/highlighter.html)
22 |
23 | API to allow creation, removal, serialization and deserialization of highlights within a page. It uses either character offsets relative to the page's `textContent` or the TextRange module's character-based features to keep track of highlight positions. This should be an improvement on the approach of the previous, unofficial Highlighter module, whose performance worsened exponentially as the number of highlights increased.
24 |
25 | ### Renamed CSS Class Applier module to Class Applier module
26 |
27 | The file name in the latest Rangy build is now rangy-classapplier.js rather than rangy-cssclassapplier.js.
28 |
29 | ### Property and method name changes
30 |
31 | The old methods are still present as aliases but are deprecated and may be removed in the future.
32 |
33 | * `rangy.createCssClassApplier()` renamed to `rangy.createClassApplier()`
34 | * `rangy.getIframeSelection()` is now redundant. Use `rangy.getSelection()` instead.
35 | * `rangy.createIframeRange()` is now redundant. Use `rangy.createRange()` instead.
36 | * `rangy.createIframeRangyRange()` is now redundant. Use `rangy.createRangyRange()` instead.
37 |
38 | ### Additions and enhancements
39 |
40 | * AMD support
41 | * NPM module called `rangy`
42 | * Bower support via [rangy-release](https://github.com/timdown/rangy-release) repository
43 | * Browserify support
44 | * Rangy's main entry functions are now much more permissive. For example, `rangy.createRange()` and `rangy.getSelection()` and all related methods now accept a `Window`, `Document` or iframe element
45 | * Added `setStartAndEnd()` convenience method to Range
46 | * Added `getBookmark()` and `moveToBookmark()` methods to Range and Selection
47 | * Added new `useExistingElements` option to class applier options object (issue 111)
48 | * Selection `setSingleRange()` now accepts an additional second parameter, `direction`
49 | * Every `backwards` Boolean API parameter replaced with string `direction` parameter. Booleans are still accepted, as are the strings "backwards", "backward", "forwards" and "forward"
50 | * Added `applyToRanges()`, `unapplyToRanges()` and `toggleRanges()` to class applier
51 | * Added `removeEmptyElements` option to class applier. When true, empty elements generated by (or appearing to be generated by) the applier are removed when applying or unapplying to a range or selection
52 | * Added `getNativeTextRange()` method to selection
53 | * Module loading improved: module script include order no longer matters, so long as rangy-core.js comes first. Modules now initialize automatically if Rangy has initialized. Also, the console now provides more helpful warning messages when modules fail to load.
54 | * New config option `alertOnFail` to allow control over whether Rangy displays an alert when it fails to initialize
55 | * New config option `autoInitialize` to allow control over whether Rangy automatically initializes when the page loads
56 | * Build script now works on non-Windows platforms
57 | * Various additions to `rangy.dom`
58 |
--------------------------------------------------------------------------------
/test/serializer.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | Serializer Test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
27 |
28 |
29 |
Serializer Test
30 |
Below is some editable content:
31 |
32 | The cabbage is a popular cultivar of a the species
33 | Brassica oleracea Linne (Capitata Group)
34 | of the Family Brassicaceae (or Cruciferae), and is used as
35 | a leafy green vegetable. It is a
36 | herbaceous,
37 | biennial,
38 | dicotyledonous
39 |
40 | flowering plant distinguished by a short stem upon
41 | which is crowded a mass of leaves, usually green but in some varieties red or purplish, which while immature
42 | form a characteristic compact, globular cluster (cabbagehead).
43 |
44 |
45 | The cabbage is a popular cultivar of a the species
46 | Brassica oleracea Linne (Capitata Group)
47 | of the Family Brassicaceae (or Cruciferae), and is used as
48 | a leafy green vegetable. It is a
49 | herbaceous,
50 | biennial,
51 | dicotyledonous
52 |
53 | flowering plant distinguished by a short stem upon
54 | which is crowded a mass of leaves, usually green but in some varieties red or purplish, which while immature
55 | form a characteristic compact, globular cluster (cabbagehead).
56 |
57 |
58 | Press the button to refresh this page and restore the current selection from the cookie:
59 |
60 |
61 |
62 |
55 | The cabbage is a popular cultivar of a the species
56 | Brassica oleracea Linne (Capitata Group)
57 | of the Family Brassicaceae (or Cruciferae), and is used as
58 | a leafy green vegetable. It is a
59 | herbaceous,
60 | biennial,
61 | dicotyledonous
62 |
63 | flowering plant distinguished by a short stem upon
64 | which is crowded a mass of leaves, usually green but in some varieties red or purplish, which while immature
65 | form a characteristic compact, globular cluster (cabbagehead). This is some area to type.
66 |
67 |
68 |
69 |
70 | Select something in the editable area above. Click on the "Save selection" button. Now click somewhere on the
71 | page to destroy the selection, and then press "Restore selection".
72 |
73 |
74 |
--------------------------------------------------------------------------------
/external/jshashtable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Tim Down.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | var Hashtable=(function(){var p="function";var n=(typeof Array.prototype.splice==p)?function(s,r){s.splice(r,1)}:function(u,t){var s,v,r;if(t===u.length-1){u.length=t}else{s=u.slice(t+1);u.length=t;for(v=0,r=s.length;v
2 |
3 |
4 | Rangy Core Demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
23 |
24 |
65 |
66 |
67 |
68 | Please use the simple editor below to create a link, demonstrating the getBoookmark() and
69 | moveToBoookmark() methods of Rangy's selection object.
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Association football is a sport played between two teams. It is usually called football, but in
81 | some countries, such as the United States, it is called soccer. In
82 | Japan, New Zealand, South Africa, Australia, Canada and
83 | Republic of Ireland, both words are commonly used.
84 |
92 |
93 |
--------------------------------------------------------------------------------
/src/modules/inactive/commands/rangy-bold_new.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Selection save and restore module for Rangy.
3 | * Bold command
4 | *
5 | * Part of Rangy, a cross-browser JavaScript range and selection library
6 | * https://github.com/timdown/rangy
7 | *
8 | * Depends on Rangy core.
9 | *
10 | * Copyright %%build:year%%, Tim Down
11 | * Licensed under the MIT license.
12 | * Version: %%build:version%%
13 | * Build date: %%build:date%%
14 | */
15 | rangy.createModule("BoldCommand", function(api, module) {
16 | api.requireModules( ["Commands"] );
17 |
18 | var dom = api.dom, commandUtil = api.Command.util;
19 | var log = log4javascript.getLogger("rangy.BoldCommand");
20 |
21 | function BoldCommand() {
22 |
23 | }
24 |
25 | api.SimpleInlineCommand.create(BoldCommand, {
26 | relevantCssProperty: "fontWeight",
27 |
28 | defaultOptions: {
29 | tagName: "b"
30 | },
31 |
32 | getSpecifiedValue: function(element) {
33 | return element.style.fontWeight || (/^(strong|b)$/i.test(element.tagName) ? "bold" : null);
34 | },
35 |
36 | valuesEqual: function(val1, val2) {
37 | val1 = ("" + val1).toLowerCase();
38 | val2 = ("" + val2).toLowerCase();
39 | return val1 == val2
40 | || (val1 == "bold" && val2 == "700")
41 | || (val2 == "bold" && val1 == "700")
42 | || (val1 == "normal" && val2 == "400")
43 | || (val2 == "normal" && val1 == "400");
44 | },
45 |
46 | createNonCssElement: function(node, value, context) {
47 | if (value == "bold" || value == "700") {
48 | return dom.getDocument(node).createElement(context.options.tagName);
49 | }
50 |
51 | return null;
52 | },
53 |
54 | isBoldCssValue: function(value) {
55 | return /^(bold|700|800|900)$/.test(value);
56 | },
57 |
58 | getRangeValue: function(range, context) {
59 | var textNodes = commandUtil.getEffectiveTextNodes(range, context), i = textNodes.length, value;
60 | log.info("getRangeValue on " + range.inspect() + ", text nodes: " + textNodes);
61 |
62 | if (textNodes.length == 0) {
63 | return this.isBoldCssValue(commandUtil.getEffectiveValue(range.commonAncestorContainer, context))
64 | } else {
65 | while (i--) {
66 | value = commandUtil.getEffectiveValue(textNodes[i], context);
67 | log.info("getRangeValue value " + value);
68 | if (!this.isBoldCssValue(value)) {
69 | log.info("getRangeValue returning false");
70 | return false;
71 | }
72 | }
73 | return true;
74 | }
75 | },
76 |
77 | getSelectionValue: function(sel, context) {
78 | var selRanges = sel.getAllRanges();
79 | for (var i = 0, len = selRanges.length; i < len; ++i) {
80 | if (!this.getRangeValue(selRanges[i], context)) {
81 | return false;
82 | }
83 | }
84 | return len > 0;
85 | },
86 |
87 | getNewRangeValue: function(range, context) {
88 | return this.getRangeValue(range, context) ? "normal" : "bold";
89 | },
90 |
91 | getNewSelectionValue: function(sel, context) {
92 | return this.getSelectionValue(sel, context) ? "normal" : "bold";
93 | },
94 |
95 | applyValueToRange: function(range, context) {
96 | var decomposed = commandUtil.decomposeRange(range, context.rangesToPreserve);
97 |
98 | log.info("applyValueToRange " + range.inspect())
99 |
100 | for (var i = 0, len = decomposed.length; i < len; ++i) {
101 | log.info("Setting node value on: " + dom.inspectNode(decomposed[i]))
102 | commandUtil.setNodeValue(decomposed[i], context);
103 | }
104 | }
105 | });
106 |
107 | api.registerCommand("bold", new BoldCommand());
108 |
109 | });
110 |
--------------------------------------------------------------------------------
/src/modules/inactive/commands/rangy-bold.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Selection save and restore module for Rangy.
3 | * Bold command
4 | *
5 | * Part of Rangy, a cross-browser JavaScript range and selection library
6 | * https://github.com/timdown/rangy
7 | *
8 | * Depends on Rangy core.
9 | *
10 | * Copyright %%build:year%%, Tim Down
11 | * Licensed under the MIT license.
12 | * Version: %%build:version%%
13 | * Build date: %%build:date%%
14 | */
15 | rangy.createModule("BoldCommand", function(api, module) {
16 | api.requireModules( ["Commands"] );
17 |
18 | var dom = api.dom, commandUtil = api.Command.util;
19 | var log = log4javascript.getLogger("rangy.BoldCommand");
20 |
21 | function BoldCommand() {
22 |
23 | }
24 |
25 | api.Command.create(BoldCommand, {
26 | relevantCssProperty: "fontWeight",
27 |
28 | defaultOptions: {
29 | tagName: "b",
30 | ignoreWhiteSpace: true
31 | },
32 |
33 | getSpecifiedValue: function(element) {
34 | return element.style.fontWeight || (/^(strong|b)$/i.test(element.tagName) ? "bold" : null);
35 | },
36 |
37 | valuesEqual: function(val1, val2) {
38 | val1 = ("" + val1).toLowerCase();
39 | val2 = ("" + val2).toLowerCase();
40 | return val1 == val2
41 | || (val1 == "bold" && val2 == "700")
42 | || (val2 == "bold" && val1 == "700")
43 | || (val1 == "normal" && val2 == "400")
44 | || (val2 == "normal" && val1 == "400");
45 | },
46 |
47 | createNonCssElement: function(node, value, context) {
48 | if (value == "bold" || value == "700") {
49 | return dom.getDocument(node).createElement(context.options.tagName);
50 | }
51 |
52 | return null;
53 | },
54 |
55 | isBoldCssValue: function(value) {
56 | return /^(bold|700|800|900)$/.test(value);
57 | },
58 |
59 | getRangeValue: function(range, context) {
60 | var textNodes = commandUtil.getEffectiveTextNodes(range, context), i = textNodes.length, value;
61 | log.info("getRangeValue on " + range.inspect() + ", text nodes: " + textNodes);
62 |
63 | if (textNodes.length == 0) {
64 | return this.isBoldCssValue(commandUtil.getEffectiveValue(range.commonAncestorContainer, context))
65 | } else {
66 | while (i--) {
67 | value = commandUtil.getEffectiveValue(textNodes[i], context);
68 | log.info("getRangeValue value " + value);
69 | if (!this.isBoldCssValue(value)) {
70 | log.info("getRangeValue returning false");
71 | return false;
72 | }
73 | }
74 | return true;
75 | }
76 | },
77 |
78 | getSelectionValue: function(sel, context) {
79 | var selRanges = sel.getAllRanges();
80 | for (var i = 0, len = selRanges.length; i < len; ++i) {
81 | if (!this.getRangeValue(selRanges[i], context)) {
82 | return false;
83 | }
84 | }
85 | return len > 0;
86 | },
87 |
88 | getNewRangeValue: function(range, context) {
89 | return this.getRangeValue(range, context) ? "normal" : "bold";
90 | },
91 |
92 | getNewSelectionValue: function(sel, context) {
93 | return this.getSelectionValue(sel, context) ? "normal" : "bold";
94 | },
95 |
96 | applyValueToRange: function(range, context) {
97 | var decomposed = commandUtil.decomposeRange(range, context.rangesToPreserve);
98 |
99 | log.info("applyValueToRange " + range.inspect())
100 |
101 | for (var i = 0, len = decomposed.length; i < len; ++i) {
102 | log.info("Setting node value on: " + dom.inspectNode(decomposed[i]))
103 | commandUtil.setNodeValue(decomposed[i], context);
104 | }
105 | }
106 | });
107 |
108 | api.registerCommand("bold", new BoldCommand());
109 |
110 | });
111 |
--------------------------------------------------------------------------------
/docs/Highlighter-Module.md:
--------------------------------------------------------------------------------
1 |
2 | [[Documentation home|Home]]
3 |
4 | # Highlighter module
5 |
6 | Provides an API for adding, removing, serializing and deserializing highlights for static HTML based on the user selection.
7 |
8 | Keeping track of highlight positions is done using a character offset-based technique.
9 |
10 | Highlighting is done using the [[class applier module|Class Applier Module]] module.
11 |
12 | You can see a live example of this module in action [here](http://lezuse.github.io/rangy/demos/highlighter.html).
13 |
14 | ## Dependencies
15 |
16 | * [[TextRange|Text Range Module]] (only required if using "TextRange" type Highlighter)
17 | * [[Class applier|Class Applier Module]]
18 |
19 | ## API
20 |
21 | ### Note about documentation formatting
22 |
23 | Square brackets around one or more function or method parameters indicate that the parameter(s) is/are optional.
24 |
25 | ----
26 |
27 | This module adds the following methods to the `rangy` object:
28 |
29 | #### `rangy.createHighlighter([Document doc], [string type])`
30 |
31 | Creates and returns a `Highlighter` object for the DOM `Document` object. If none is supplied, the current document is used.
32 |
33 | The `type` parameter determines which algorithm is used for converting ranges to and from character offsets. It can have either of the following values:
34 |
35 | * *textContent*: A simple algorithm for generating character offsets from ranges based on all the text node content in the document. This approach has the advantage of being fast and reliable but the disadvantages of being susceptible to any changes to text nodes within the DOM, including collapsed white space characters and those in hidden elements, and of being much less likely to generate offsets that work in a browser other than the one in which they were created.
36 |
37 | * *TextRange*: If this value is used, the [[TextRange module|Text Range Module]] must be loaded. This is a more complicated approach. It has the advantage of being designed to take into account only characters that are visible on the page, making it more resilient to insignificant DOM changes and more likely to generate offsets that work in browsers other than the one in which they were created. However, it is slow. *Very* slow on a large DOM.
38 |
39 | The default is "textContent".
40 |
41 | ----
42 |
43 | ## Highlighter API
44 |
45 | #### `addClassApplier(ClassApplier applier)`
46 |
47 | Adds a [[class applier|Class Applier Module]] that this highlighter can use to display highlights.
48 |
49 | ----
50 |
51 | #### `highlightSelection(string className, [Object options])`
52 |
53 | Highlights the current selection (or `options.selection`, if supplied), applying the class `className`. Any existing highlights that overlap the selection are merged into the new highlight, even if they are not using the same class.
54 |
55 | A class applier for `className` must have previously been added to the highlighter using the `addClassApplier()` method.
56 |
57 | Each property of the (optional) `options` parameter object is optional and uses the appropriate default value if not specified. The available properties are:
58 |
59 | * `selection`: The Rangy selection object to use for the new highlight. Defaults to the current selection in the document in which Rangy is running.
60 | * `exclusive`: Boolean. When set to `true`, this property prevents overlapping highlights. Defaults to `true`.
61 | * `containerElementId`: String. When specified, the highlight created is scoped to the element with this `id`, meaning that changes to other parts of the DOM cannot affect this highlight.
62 |
63 | ----
64 |
65 | #### `unhighlightSelection([Selection selection])`
66 |
67 | Removes all highlights that overlap the current selection (or `selection`, if supplied).
68 |
69 | ----
70 |
71 | #### `removeAllHighlights()`
72 |
73 | Removes all highlights.
74 |
75 | ----
76 |
77 | #### `serialize([Selection selection])`
78 |
79 | Serializes all highlights into a single string. This string may be passed into the `deserialize()` method on a different page visit.
80 |
81 | ----
82 |
83 | #### `deserialize(string serialized)`
84 |
85 | Recreates highlights that were previously serialized using the `serialize()` method.
86 |
87 | ----
88 |
89 | #### `getHighlightForElement(Element el)`
90 |
91 | Retrieves the `Highlight` object that contains the contents of element `el`.
--------------------------------------------------------------------------------
/demos/scopedhighlights.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rangy Highlighter Module Demo
5 |
6 |
11 |
12 |
13 |
14 |
76 |
77 |
78 |
79 |
Highlighter
80 |
Make a selection in the document and use the buttons below to highlight and unhighlight.
81 |
82 |
83 |
84 |
Preserving highlights between page requests
85 |
92 |
93 |
94 |
95 |
Highlighter module scoped highlights demo
96 |
97 | Please use your mouse and/or keyboard to make selections from the sample quotes below and use the buttons
98 | on the left hand size to create and remove highlights.
99 |
100 |
101 |
"Hello. My name is Inigo Montoya. You killed my father. Prepare to die."
102 |
"You keep using that word. I do not think it means what you think it means."
103 |
"I do not mean to pry, but you don't by any chance happen to have six fingers on your right hand?"
104 |
"Oh, there's something I ought to tell you. I'm not left-handed either."
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/docs/New-Features-In-One-Point-Two.md:
--------------------------------------------------------------------------------
1 |
2 | # New features in Rangy 1.2
3 |
4 | ## Summary
5 |
6 | ### CSS Class Applier module improvements
7 |
8 | * Added option to use elements other than `` for surrounding text
9 | * Added option (enabled by default) to ignore non-significant white space nodes (for example, a line break and indentation between two `
` elements is ignored while a space between two `` elements is not)
10 | * Added option to apply CSS classes to editable content only (disabled by default)
11 | * Range and Selection prototype objects exposed, allowing extension
12 | * Some major bugfixes (issue 54, issue 57, issue 60, issue 61 and issue 64).
13 |
14 | ### Core additions
15 |
16 | * Added `toHtml()` method to both Range and Selection
17 | * Added `union()` method to Range
18 | * Added `isValid()` method to Range
19 | * Added `containsNodeText()` and `containsRange()` methods to Range
20 | * Added option to allow IE 9 to use legacy `TextRange` and `document.selection` objects rather than standard W3C/WHATWG Range and Selection
21 | * Added `rangy.version` property
22 |
23 | ### Issues fixed
24 |
25 | Issue 51, issue 53, issue 54, issue 57, issue 58, issue 60, issue 61, issue 62 and issue 64.
26 |
27 | ## CSS Class Applier change details
28 |
29 | The second parameter in `rangy.createCssClassApplier()` is now an options object. It's backwards compatible, so providing a Boolean value will set the `normalize` option as before.
30 |
31 | ### `rangy.createCssClassApplier(String cssClass[, Object options[, Array tagNames]])`
32 |
33 | Creates and returns a `CssClassApplier` object. Elements created by this object are given the CSS class `cssClass`. The available properties of the `options` parameter object are as follows:
34 |
35 | * `elementTagName`: The tag name of the element to use for surrounding text. This may be any inline element that may contain text. The default is "span".
36 | * `elementProperties`: An object containing properties that are copied to each element created to surround text
37 | * `ignoreWhiteSpace`: Boolean value indicating whether to ignore insignificant white space text nodes (such as a line break and/or indentation between `
` tags in the HTML source of the page). The default is `true`.
38 | * `applyToEditableOnly`: Boolean value indicating whether to only apply the CSS class to editable content (`contentEditable` true or `designMode` "on"). The default is `false`.
39 | * `normalize`: As per the `normalize` function parameter in Rangy 1.1.
40 |
41 | #### Example
42 |
43 | The following will create a CSS class applier that will surround a range or selection within a link (or links) to the Rangy home page, with class "someClass":
44 |
45 | ```
46 | var applier = rangy.createCssClassApplier("someClass", {
47 | elementTagName: "a",
48 | elementProperties: {
49 | href: "http://code.google.com/p/rangy",
50 | title: "Rangy home page"
51 | }
52 | });
53 | ```
54 |
55 | ## Core change details
56 |
57 | ### `toHtml()`
58 |
59 | Called on a Range or Selection object. Returns a string containing an HTML representation of the current Range or Selection.
60 |
61 | ### `union(Range range)`
62 |
63 | Called on a Range object. Providing `range` overlaps or touches the current range, returns the smallest possible Range containing the whole of both ranges, or null otherwise.
64 |
65 | ### `isValid()`
66 |
67 | Called on a Range object. Returns a Boolean that is true if the range start and end boundaries are valid, i.e. the boundary container nodes still exist in the document and the boundary offsets are valid for the boundary nodes.
68 |
69 | ### `containsNodeText(Node node)`
70 |
71 | Called on a Range object. Returns a Boolean representing whether the range contains all of the text (within text nodes) contained within `node`. This is to provide an intuitive means of checking whether a range "contains" a node if you consider the range as a visible text selection on the page, without having to worry about niggly details about range boundaries.
72 |
73 | ### `containsRange(Range range)`
74 |
75 | Called on a Range object. Convenience method that returns a Boolean representing whether the current range completely contains `range`.
76 |
77 | ### `rangy.config.preferTextRange`
78 |
79 | Setting this option to `true` causes Rangy to use legacy `TextRange` and `document.selection` objects internally in IE 9 rather than standard Range and Selection objects. You can use this option if you need Rangy in IE 9 to behave the same as in previous versions of IE.
80 |
81 | ### Range and Selection prototype objects
82 |
83 | Prototype objects for Range and Selection are now exposed as `rangy.rangePrototype` and `rangy.selectionPrototype`. This allows custom extension of all ranges and selections.
--------------------------------------------------------------------------------
/test/domrange.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
A paragraph with bold text with italic words plus some using both.
70 |
A paragraph with bold text with italic words plus some using both.
71 | A paragraph with bold text with italic words
72 |
73 | plus some using both.
74 |
75 |
a
76 |
b
77 |
78 |
79 |
80 |
81 |
82 |
A paragraph with bold text with italic words plus some using both.
83 | A paragraph with bold text with bold italic text and italic words plus some using both.
84 |
85 |
86 |
a
87 |
b
88 |
89 |
90 |
91 | Some preformatted how it'll do
92 |
93 | with this, plus some line breaks
94 |
95 |
96 |
97 |
one
two
one
two
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/fiddlings/spec/innerText_files/dfn.js:
--------------------------------------------------------------------------------
1 | // dfn.js
2 | // makes elements link back to all uses of the term
3 | // no copyright is asserted on this file
4 |
5 | var dfnTimer = new Date();
6 |
7 | var dfnMapTarget = -1;
8 | var dfnMapDone = false;
9 | var dfnMap = {};
10 | function initDfn() {
11 | var links = [];
12 | dfnMapTarget = document.links.length;
13 | for (var i = 0; i < dfnMapTarget; i += 1)
14 | links[i] = document.links[i];
15 | var k = 0;
16 | var n = 0;
17 | var initDfnInternal = function () {
18 | n += 1;
19 | var start = new Date();
20 | while (k < dfnMapTarget) {
21 | if (links[k].hash.length > 1) {
22 | if (links[k].className != "no-backref" && links[k].parentNode.className != "no-backref") {
23 | var s = links[k].hash.substr(1);
24 | if (!(s in dfnMap))
25 | dfnMap[s] = [];
26 | dfnMap[s].push(links[k]);
27 | }
28 | }
29 | k += 1;
30 | if (new Date() - start > 1000) {
31 | setTimeout(initDfnInternal, 10000);
32 | return;
33 | }
34 | }
35 | dfnMapDone = true;
36 | document.body.className += " dfnEnabled";
37 | if (getCookie('profile') == '1')
38 | document.getElementsByTagName('h2')[0].textContent += '; dfn.js: ' + (new Date() - dfnTimer) + 'ms to do ' + dfnMapTarget + ' links in ' + n + ' loops';
39 | }
40 | initDfnInternal();
41 | }
42 |
43 | var dfnPanel;
44 | var dfnUniqueId = 0;
45 | var dfnTimeout;
46 | document.addEventListener('click', dfnShow, false);
47 | function dfnShow(event) {
48 | if (dfnTimeout) {
49 | clearTimeout(dfnTimeout);
50 | dfnTimeout = null;
51 | }
52 | if (dfnPanel) {
53 | dfnPanel.parentNode.removeChild(dfnPanel);
54 | dfnPanel = null;
55 | }
56 | if (dfnMapDone) {
57 | var node = event.target;
58 | while (node && (node.nodeType != event.target.ELEMENT_NODE || node.tagName != "DFN"))
59 | node = node.parentNode;
60 | if (node) {
61 | var panel = document.createElement('div');
62 | panel.className = 'dfnPanel';
63 | if (node.id) {
64 | var permalinkP = document.createElement('p');
65 | var permalinkA = document.createElement('a');
66 | permalinkA.href = '#' + node.id;
67 | permalinkA.textContent = '#' + node.id;
68 | permalinkP.appendChild(permalinkA);
69 | panel.appendChild(permalinkP);
70 | }
71 | var p = document.createElement('p');
72 | panel.appendChild(p);
73 | if (node.id in dfnMap || node.parentNode.id in dfnMap) {
74 | p.textContent = 'Referenced in:';
75 | var ul = document.createElement('ul');
76 | var lastHeader;
77 | var lastLi;
78 | var n;
79 | var sourceLinks = [];
80 | if (node.id in dfnMap)
81 | for (var i = 0; i < dfnMap[node.id].length; i += 1)
82 | sourceLinks.push(dfnMap[node.id][i]);
83 | if (node.parentNode.id in dfnMap)
84 | for (var i = 0; i < dfnMap[node.parentNode.id].length; i += 1)
85 | sourceLinks.push(dfnMap[node.parentNode.id][i]);
86 | for (var i = 0; i < sourceLinks.length; i += 1) {
87 | var link = sourceLinks[i];
88 | var header = dfnGetCaption(link);
89 | var a = document.createElement('a');
90 | if (!link.id)
91 | link.id = 'dfnReturnLink-' + dfnUniqueId++;
92 | a.href = '#' + link.id;
93 | if (header != lastHeader) {
94 | lastHeader = header;
95 | n = 1;
96 | var li = document.createElement('li');
97 | var cloneHeader = header.cloneNode(true);
98 | while (cloneHeader.hasChildNodes())
99 | a.appendChild(cloneHeader.firstChild);
100 | lastLi = li;
101 | li.appendChild(a);
102 | ul.appendChild(li);
103 | } else {
104 | n += 1;
105 | a.appendChild(document.createTextNode('(' + n + ')'));
106 | lastLi.appendChild(document.createTextNode(' '));
107 | lastLi.appendChild(a);
108 | }
109 | }
110 | panel.appendChild(ul);
111 | } else {
112 | p.textContent = 'No references in this file.';
113 | }
114 | node.appendChild(panel);
115 | dfnPanel = panel;
116 | }
117 | } else {
118 | dfnTimeout = setTimeout(dfnShow, 250, event);
119 | }
120 | }
121 |
122 | function dfnGetCaption(link) {
123 | var node = link;
124 | while (node) {
125 | if (node.nodeType == node.ELEMENT_NODE && node.tagName.match(/^H[1-6]$/)) {
126 | return node;
127 | } else if (!node.previousSibling) {
128 | node = node.parentNode;
129 | } else {
130 | node = node.previousSibling;
131 | if (node.nodeType == node.ELEMENT_NODE && node.className == "impl") {
132 | node = node.lastChild;
133 | }
134 | }
135 | }
136 | return null;
137 | }
138 |
139 | // setup (disabled for multipage copy)
140 | if (document.getElementById('head'))
141 | initDfn();
142 |
--------------------------------------------------------------------------------
/demos/bold.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Rangy Bold Demo
5 |
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
46 |
47 |
48 |
49 |
Add / remove CSS classes to / from selection
50 | Make a selection in the document on the right and use the buttons below to toggle CSS classes on the selection:
51 |
52 |
53 |
55 |
56 |
57 |
58 |
Rangy Text Commands Module Demo
59 |
60 |
61 |
62 |
63 | Please use your mouse and/or keyboard to make selections from the sample content below and use the buttons on
64 | the left hand size to toggle CSS classes applied to text content within the selection.
65 |
66 |
67 |
68 | Association football is a sport played between two teams. It is usually called football, but in
69 | some countries, such as the United States, it is called soccer. In
70 | Japan, New Zealand, South Africa, Australia, Canada and
71 | Republic of Ireland, both words are commonly used.
72 |
73 |
74 | Each team has 11 players on the field. One of these players is the goalkeeper, and the other ten are
75 | known as "outfield players." The game is played by kicking a ball into the opponent's goal. A
76 | match has 90 minutes of play, with a break of 15 minutes in the middle. The break in the middle is called
77 | half-time.
78 |
79 |
Competitions (this section is editable)
80 |
81 | There are many competitions for football, for both football clubs and countries. Football clubs usually play
82 | other teams in their own country, with a few exceptions. Cardiff City F.C. from Wales for example, play
83 | in the English leagues and in the English FA Cup.
84 |
85 |
Who plays football (this section is editable and in pre-formatted text)
86 |
87 | Football is the world's most popular sport. It is played in more
88 | countries than any other game. In fact, FIFA (the Federation
89 | Internationale de Football Association) has more members than the
90 | United Nations.
91 |
92 | It is played by both males and females.
93 |
94 |
95 |
104 |
105 |
--------------------------------------------------------------------------------
/docs/Dom-Utils.md:
--------------------------------------------------------------------------------
1 | # DOM-related utility functions
2 |
3 | Rangy uses some DOM helper functions internally, and provides these functions to other code via `rangy.dom`.
4 |
5 | ----
6 |
7 | ### Note about documentation formatting
8 |
9 | Square brackets around one or more function or method parameters indicate that the parameter(s) is/are optional.
10 |
11 | ----
12 |
13 | #### `rangy.dom.DomPosition(Node node, Number offset)`
14 |
15 | Constructor function that creates an object representing a position within a document in terms of a DOM node and an offset within that node. For character nodes such as text, comment and CDATA nodes, this is a character offset within their text content. For all other nodes, the offset is the index of a child node within that node.
16 |
17 | `DomPosition` objects have the following methods:
18 |
19 | * `inspect()`: returns a string representation of the position, such as `[DomPosition(
:1)]`.
20 | * `equals(DomPosition pos)`: returns a Boolean indicating whether this position is identical to the specified position.
21 |
22 | ----
23 |
24 | #### `rangy.dom.comparePoints(Node nodeA, Number offsetA, Node nodeB, Number offsetB)`
25 |
26 | Compares two positions (A and B) within a document and returns -1 if A is earlier in the document than B, 0 if A and B are identical or 1 if A is later in the document than B.
27 |
28 | ----
29 |
30 | #### `rangy.dom.getBody(Document doc)`
31 |
32 | Returns a reference to the body element of the specified document.
33 |
34 | ----
35 |
36 | #### `rangy.dom.getClosestAncestorIn(Node node, Node ancestor[, Boolean selfIsAncestor])`
37 |
38 | Returns a reference to the outermost node contained within `ancestor` that is an ancestor of `node`. Whether `node` is considered an ancestor of itself and is therefore a valid return value is determined by `selfIsAncestor`.
39 |
40 | ----
41 |
42 | #### `rangy.dom.getCommonAncestor(Node node1, Node node2)`
43 |
44 | Returns a reference to the innermost node that is an ancestor of both `node1` and `node2`.
45 |
46 | ----
47 |
48 | #### `rangy.dom.getDocument(Node node)`
49 |
50 | Returns a reference to the document that contains `node`.
51 |
52 | ----
53 |
54 | #### `rangy.dom.getNodeIndex(Node node)`
55 |
56 | Returns the index of the specified node within its parent.
57 |
58 | ----
59 |
60 | #### `rangy.dom.getNodeLength(Node node)`
61 |
62 | Returns the length of the specified node. For text and comment nodes, this is the length of the text the node represents. For any other node type, this is the number of child nodes of the node.
63 |
64 | ----
65 |
66 | #### `rangy.dom.getWindow(Node node)`
67 |
68 | Returns a reference to the Window object that contains `node`'s document.
69 |
70 | ----
71 |
72 | #### `rangy.dom.insertAfter(Node node, Node precedingNode)`
73 |
74 | Inserts the `node` immediately after `precedingNode` in the document and returns a reference to the inserted node.
75 |
76 | ----
77 |
78 | #### `rangy.dom.inspectNode(Node node)`
79 |
80 | Returns a string representation of the node for debugging purposes.
81 |
82 | ----
83 |
84 | #### `rangy.dom.isAncestorOf(Node ancestor, Node descendant)`
85 |
86 | Returns a Boolean indicating whether `ancestor` is an ancestor of `descendant`.
87 |
88 | ----
89 |
90 | #### `rangy.dom.isOrIsAncestorOf(Node ancestor, Node descendant)`
91 |
92 | Returns a Boolean indicating whether `ancestor` equals or is an ancestor of `descendant`.
93 |
94 | ----
95 |
96 | #### `rangy.dom.isCharacterDataNode(Node node)`
97 |
98 | Returns `true` if `node` is either a text node, comment node or CDATA node and `false` otherwise.
99 |
100 | ----
101 |
102 | #### `rangy.dom.splitDataNode(Node node, Number index)`
103 |
104 | Splits a character node into two at the character index specified by `index` and returns a reference to the newly created node, which is the node representing the content after the splitting point.
105 |
106 | ----
107 |
108 | #### `rangy.dom.getIframeWindow(Element iframeEl)`
109 |
110 | ##### From v1.1
111 |
112 | Returns the Window object for the specified `