rangy-rails 1.3alpha.804.0 → 1.3.1.pre.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,233 +3,250 @@
3
3
  * Saves and restores user selections using marker invisible elements in the DOM.
4
4
  *
5
5
  * Part of Rangy, a cross-browser JavaScript range and selection library
6
- * http://code.google.com/p/rangy/
6
+ * https://github.com/timdown/rangy
7
7
  *
8
8
  * Depends on Rangy core.
9
9
  *
10
- * Copyright 2013, Tim Down
10
+ * Copyright 2015, Tim Down
11
11
  * Licensed under the MIT license.
12
- * Version: 1.3alpha.804
13
- * Build date: 8 December 2013
12
+ * Version: 1.3.1-dev
13
+ * Build date: 20 May 2015
14
14
  */
15
- rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
16
- var dom = api.dom;
17
-
18
- var markerTextChar = "\ufeff";
19
-
20
- function gEBI(id, doc) {
21
- return (doc || document).getElementById(id);
15
+ (function(factory, root) {
16
+ if (typeof define == "function" && define.amd) {
17
+ // AMD. Register as an anonymous module with a dependency on Rangy.
18
+ define(["./rangy-core"], factory);
19
+ } else if (typeof module != "undefined" && typeof exports == "object") {
20
+ // Node/CommonJS style
21
+ module.exports = factory( require("rangy") );
22
+ } else {
23
+ // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
24
+ factory(root.rangy);
22
25
  }
23
-
24
- function insertRangeBoundaryMarker(range, atStart) {
25
- var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
26
- var markerEl;
27
- var doc = dom.getDocument(range.startContainer);
28
-
29
- // Clone the Range and collapse to the appropriate boundary point
30
- var boundaryRange = range.cloneRange();
31
- boundaryRange.collapse(atStart);
32
-
33
- // Create the marker element containing a single invisible character using DOM methods and insert it
34
- markerEl = doc.createElement("span");
35
- markerEl.id = markerId;
36
- markerEl.style.lineHeight = "0";
37
- markerEl.style.display = "none";
38
- markerEl.className = "rangySelectionBoundary";
39
- markerEl.appendChild(doc.createTextNode(markerTextChar));
40
-
41
- boundaryRange.insertNode(markerEl);
42
- boundaryRange.detach();
43
- return markerEl;
44
- }
45
-
46
- function setRangeBoundary(doc, range, markerId, atStart) {
47
- var markerEl = gEBI(markerId, doc);
48
- if (markerEl) {
49
- range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
50
- markerEl.parentNode.removeChild(markerEl);
51
- } else {
52
- module.warn("Marker element has been removed. Cannot restore selection.");
26
+ })(function(rangy) {
27
+ rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
28
+ var dom = api.dom;
29
+ var removeNode = dom.removeNode;
30
+ var isDirectionBackward = api.Selection.isDirectionBackward;
31
+ var markerTextChar = "\ufeff";
32
+
33
+ function gEBI(id, doc) {
34
+ return (doc || document).getElementById(id);
53
35
  }
54
- }
55
-
56
- function compareRanges(r1, r2) {
57
- return r2.compareBoundaryPoints(r1.START_TO_START, r1);
58
- }
59
36
 
60
- function saveRange(range, backward) {
61
- var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
37
+ function insertRangeBoundaryMarker(range, atStart) {
38
+ var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
39
+ var markerEl;
40
+ var doc = dom.getDocument(range.startContainer);
41
+
42
+ // Clone the Range and collapse to the appropriate boundary point
43
+ var boundaryRange = range.cloneRange();
44
+ boundaryRange.collapse(atStart);
45
+
46
+ // Create the marker element containing a single invisible character using DOM methods and insert it
47
+ markerEl = doc.createElement("span");
48
+ markerEl.id = markerId;
49
+ markerEl.style.lineHeight = "0";
50
+ markerEl.style.display = "none";
51
+ markerEl.className = "rangySelectionBoundary";
52
+ markerEl.appendChild(doc.createTextNode(markerTextChar));
53
+
54
+ boundaryRange.insertNode(markerEl);
55
+ return markerEl;
56
+ }
62
57
 
63
- if (range.collapsed) {
64
- endEl = insertRangeBoundaryMarker(range, false);
65
- return {
66
- document: doc,
67
- markerId: endEl.id,
68
- collapsed: true
69
- };
70
- } else {
71
- endEl = insertRangeBoundaryMarker(range, false);
72
- startEl = insertRangeBoundaryMarker(range, true);
58
+ function setRangeBoundary(doc, range, markerId, atStart) {
59
+ var markerEl = gEBI(markerId, doc);
60
+ if (markerEl) {
61
+ range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
62
+ removeNode(markerEl);
63
+ } else {
64
+ module.warn("Marker element has been removed. Cannot restore selection.");
65
+ }
66
+ }
73
67
 
74
- return {
75
- document: doc,
76
- startMarkerId: startEl.id,
77
- endMarkerId: endEl.id,
78
- collapsed: false,
79
- backward: backward,
80
- toString: function() {
81
- return "original text: '" + text + "', new text: '" + range.toString() + "'";
82
- }
83
- };
68
+ function compareRanges(r1, r2) {
69
+ return r2.compareBoundaryPoints(r1.START_TO_START, r1);
84
70
  }
85
- }
86
71
 
87
- function restoreRange(rangeInfo, normalize) {
88
- var doc = rangeInfo.document;
89
- if (typeof normalize == "undefined") {
90
- normalize = true;
72
+ function saveRange(range, direction) {
73
+ var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
74
+ var backward = isDirectionBackward(direction);
75
+
76
+ if (range.collapsed) {
77
+ endEl = insertRangeBoundaryMarker(range, false);
78
+ return {
79
+ document: doc,
80
+ markerId: endEl.id,
81
+ collapsed: true
82
+ };
83
+ } else {
84
+ endEl = insertRangeBoundaryMarker(range, false);
85
+ startEl = insertRangeBoundaryMarker(range, true);
86
+
87
+ return {
88
+ document: doc,
89
+ startMarkerId: startEl.id,
90
+ endMarkerId: endEl.id,
91
+ collapsed: false,
92
+ backward: backward,
93
+ toString: function() {
94
+ return "original text: '" + text + "', new text: '" + range.toString() + "'";
95
+ }
96
+ };
97
+ }
91
98
  }
92
- var range = api.createRange(doc);
93
- if (rangeInfo.collapsed) {
94
- var markerEl = gEBI(rangeInfo.markerId, doc);
95
- if (markerEl) {
96
- markerEl.style.display = "inline";
97
- var previousNode = markerEl.previousSibling;
98
99
 
99
- // Workaround for issue 17
100
- if (previousNode && previousNode.nodeType == 3) {
101
- markerEl.parentNode.removeChild(markerEl);
102
- range.collapseToPoint(previousNode, previousNode.length);
100
+ function restoreRange(rangeInfo, normalize) {
101
+ var doc = rangeInfo.document;
102
+ if (typeof normalize == "undefined") {
103
+ normalize = true;
104
+ }
105
+ var range = api.createRange(doc);
106
+ if (rangeInfo.collapsed) {
107
+ var markerEl = gEBI(rangeInfo.markerId, doc);
108
+ if (markerEl) {
109
+ markerEl.style.display = "inline";
110
+ var previousNode = markerEl.previousSibling;
111
+
112
+ // Workaround for issue 17
113
+ if (previousNode && previousNode.nodeType == 3) {
114
+ removeNode(markerEl);
115
+ range.collapseToPoint(previousNode, previousNode.length);
116
+ } else {
117
+ range.collapseBefore(markerEl);
118
+ removeNode(markerEl);
119
+ }
103
120
  } else {
104
- range.collapseBefore(markerEl);
105
- markerEl.parentNode.removeChild(markerEl);
121
+ module.warn("Marker element has been removed. Cannot restore selection.");
106
122
  }
107
123
  } else {
108
- module.warn("Marker element has been removed. Cannot restore selection.");
124
+ setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
125
+ setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
109
126
  }
110
- } else {
111
- setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
112
- setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
113
- }
114
127
 
115
- if (normalize) {
116
- range.normalizeBoundaries();
117
- }
128
+ if (normalize) {
129
+ range.normalizeBoundaries();
130
+ }
118
131
 
119
- return range;
120
- }
132
+ return range;
133
+ }
121
134
 
122
- function saveRanges(ranges, backward) {
123
- var rangeInfos = [], range, doc;
135
+ function saveRanges(ranges, direction) {
136
+ var rangeInfos = [], range, doc;
137
+ var backward = isDirectionBackward(direction);
124
138
 
125
- // Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
126
- ranges = ranges.slice(0);
127
- ranges.sort(compareRanges);
139
+ // Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
140
+ ranges = ranges.slice(0);
141
+ ranges.sort(compareRanges);
128
142
 
129
- for (var i = 0, len = ranges.length; i < len; ++i) {
130
- rangeInfos[i] = saveRange(ranges[i], backward);
131
- }
143
+ for (var i = 0, len = ranges.length; i < len; ++i) {
144
+ rangeInfos[i] = saveRange(ranges[i], backward);
145
+ }
132
146
 
133
- // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
134
- // between its markers
135
- for (i = len - 1; i >= 0; --i) {
136
- range = ranges[i];
137
- doc = api.DomRange.getRangeDocument(range);
138
- if (range.collapsed) {
139
- range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
140
- } else {
141
- range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
142
- range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
147
+ // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
148
+ // between its markers
149
+ for (i = len - 1; i >= 0; --i) {
150
+ range = ranges[i];
151
+ doc = api.DomRange.getRangeDocument(range);
152
+ if (range.collapsed) {
153
+ range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
154
+ } else {
155
+ range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
156
+ range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
157
+ }
143
158
  }
159
+
160
+ return rangeInfos;
144
161
  }
145
162
 
146
- return rangeInfos;
147
- }
163
+ function saveSelection(win) {
164
+ if (!api.isSelectionValid(win)) {
165
+ module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
166
+ return null;
167
+ }
168
+ var sel = api.getSelection(win);
169
+ var ranges = sel.getAllRanges();
170
+ var backward = (ranges.length == 1 && sel.isBackward());
148
171
 
149
- function saveSelection(win) {
150
- if (!api.isSelectionValid(win)) {
151
- module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
152
- return null;
153
- }
154
- var sel = api.getSelection(win);
155
- var ranges = sel.getAllRanges();
156
- var backward = (ranges.length == 1 && sel.isBackward());
172
+ var rangeInfos = saveRanges(ranges, backward);
157
173
 
158
- var rangeInfos = saveRanges(ranges, backward);
174
+ // Ensure current selection is unaffected
175
+ if (backward) {
176
+ sel.setSingleRange(ranges[0], backward);
177
+ } else {
178
+ sel.setRanges(ranges);
179
+ }
159
180
 
160
- // Ensure current selection is unaffected
161
- if (backward) {
162
- sel.setSingleRange(ranges[0], "backward");
163
- } else {
164
- sel.setRanges(ranges);
181
+ return {
182
+ win: win,
183
+ rangeInfos: rangeInfos,
184
+ restored: false
185
+ };
165
186
  }
166
187
 
167
- return {
168
- win: win,
169
- rangeInfos: rangeInfos,
170
- restored: false
171
- };
172
- }
188
+ function restoreRanges(rangeInfos) {
189
+ var ranges = [];
173
190
 
174
- function restoreRanges(rangeInfos) {
175
- var ranges = [];
191
+ // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
192
+ // normalization affecting previously restored ranges.
193
+ var rangeCount = rangeInfos.length;
176
194
 
177
- // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
178
- // normalization affecting previously restored ranges.
179
- var rangeCount = rangeInfos.length;
195
+ for (var i = rangeCount - 1; i >= 0; i--) {
196
+ ranges[i] = restoreRange(rangeInfos[i], true);
197
+ }
180
198
 
181
- for (var i = rangeCount - 1; i >= 0; i--) {
182
- ranges[i] = restoreRange(rangeInfos[i], true);
199
+ return ranges;
183
200
  }
184
201
 
185
- return ranges;
186
- }
202
+ function restoreSelection(savedSelection, preserveDirection) {
203
+ if (!savedSelection.restored) {
204
+ var rangeInfos = savedSelection.rangeInfos;
205
+ var sel = api.getSelection(savedSelection.win);
206
+ var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
187
207
 
188
- function restoreSelection(savedSelection, preserveDirection) {
189
- if (!savedSelection.restored) {
190
- var rangeInfos = savedSelection.rangeInfos;
191
- var sel = api.getSelection(savedSelection.win);
192
- var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
208
+ if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
209
+ sel.removeAllRanges();
210
+ sel.addRange(ranges[0], true);
211
+ } else {
212
+ sel.setRanges(ranges);
213
+ }
193
214
 
194
- if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
195
- sel.removeAllRanges();
196
- sel.addRange(ranges[0], true);
197
- } else {
198
- sel.setRanges(ranges);
215
+ savedSelection.restored = true;
199
216
  }
200
-
201
- savedSelection.restored = true;
202
217
  }
203
- }
204
218
 
205
- function removeMarkerElement(doc, markerId) {
206
- var markerEl = gEBI(markerId, doc);
207
- if (markerEl) {
208
- markerEl.parentNode.removeChild(markerEl);
219
+ function removeMarkerElement(doc, markerId) {
220
+ var markerEl = gEBI(markerId, doc);
221
+ if (markerEl) {
222
+ removeNode(markerEl);
223
+ }
209
224
  }
210
- }
211
225
 
212
- function removeMarkers(savedSelection) {
213
- var rangeInfos = savedSelection.rangeInfos;
214
- for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
215
- rangeInfo = rangeInfos[i];
216
- if (rangeInfo.collapsed) {
217
- removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
218
- } else {
219
- removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
220
- removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
226
+ function removeMarkers(savedSelection) {
227
+ var rangeInfos = savedSelection.rangeInfos;
228
+ for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
229
+ rangeInfo = rangeInfos[i];
230
+ if (rangeInfo.collapsed) {
231
+ removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
232
+ } else {
233
+ removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
234
+ removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
235
+ }
221
236
  }
222
237
  }
223
- }
224
238
 
225
- api.util.extend(api, {
226
- saveRange: saveRange,
227
- restoreRange: restoreRange,
228
- saveRanges: saveRanges,
229
- restoreRanges: restoreRanges,
230
- saveSelection: saveSelection,
231
- restoreSelection: restoreSelection,
232
- removeMarkerElement: removeMarkerElement,
233
- removeMarkers: removeMarkers
239
+ api.util.extend(api, {
240
+ saveRange: saveRange,
241
+ restoreRange: restoreRange,
242
+ saveRanges: saveRanges,
243
+ restoreRanges: restoreRanges,
244
+ saveSelection: saveSelection,
245
+ restoreSelection: restoreSelection,
246
+ removeMarkerElement: removeMarkerElement,
247
+ removeMarkers: removeMarkers
248
+ });
234
249
  });
235
- });
250
+
251
+ return rangy;
252
+ }, this);
@@ -4,292 +4,311 @@
4
4
  * cookie or local storage and restore it on the user's next visit to the same page.
5
5
  *
6
6
  * Part of Rangy, a cross-browser JavaScript range and selection library
7
- * http://code.google.com/p/rangy/
7
+ * https://github.com/timdown/rangy
8
8
  *
9
9
  * Depends on Rangy core.
10
10
  *
11
- * Copyright 2013, Tim Down
11
+ * Copyright 2015, Tim Down
12
12
  * Licensed under the MIT license.
13
- * Version: 1.3alpha.804
14
- * Build date: 8 December 2013
13
+ * Version: 1.3.1-dev
14
+ * Build date: 20 May 2015
15
15
  */
16
- rangy.createModule("Serializer", ["WrappedSelection"], function(api, module) {
17
- var UNDEF = "undefined";
18
-
19
- // encodeURIComponent and decodeURIComponent are required for cookie handling
20
- if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
21
- module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");
16
+ (function(factory, root) {
17
+ if (typeof define == "function" && define.amd) {
18
+ // AMD. Register as an anonymous module with a dependency on Rangy.
19
+ define(["./rangy-core"], factory);
20
+ } else if (typeof module != "undefined" && typeof exports == "object") {
21
+ // Node/CommonJS style
22
+ module.exports = factory( require("rangy") );
23
+ } else {
24
+ // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
25
+ factory(root.rangy);
22
26
  }
23
-
24
- // Checksum for checking whether range can be serialized
25
- var crc32 = (function() {
26
- function utf8encode(str) {
27
- var utf8CharCodes = [];
28
-
29
- for (var i = 0, len = str.length, c; i < len; ++i) {
30
- c = str.charCodeAt(i);
31
- if (c < 128) {
32
- utf8CharCodes.push(c);
33
- } else if (c < 2048) {
34
- utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
35
- } else {
36
- utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
37
- }
38
- }
39
- return utf8CharCodes;
27
+ })(function(rangy) {
28
+ rangy.createModule("Serializer", ["WrappedSelection"], function(api, module) {
29
+ var UNDEF = "undefined";
30
+ var util = api.util;
31
+
32
+ // encodeURIComponent and decodeURIComponent are required for cookie handling
33
+ if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
34
+ module.fail("encodeURIComponent and/or decodeURIComponent method is missing");
40
35
  }
41
36
 
42
- var cachedCrcTable = null;
43
-
44
- function buildCRCTable() {
45
- var table = [];
46
- for (var i = 0, j, crc; i < 256; ++i) {
47
- crc = i;
48
- j = 8;
49
- while (j--) {
50
- if ((crc & 1) == 1) {
51
- crc = (crc >>> 1) ^ 0xEDB88320;
37
+ // Checksum for checking whether range can be serialized
38
+ var crc32 = (function() {
39
+ function utf8encode(str) {
40
+ var utf8CharCodes = [];
41
+
42
+ for (var i = 0, len = str.length, c; i < len; ++i) {
43
+ c = str.charCodeAt(i);
44
+ if (c < 128) {
45
+ utf8CharCodes.push(c);
46
+ } else if (c < 2048) {
47
+ utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
52
48
  } else {
53
- crc >>>= 1;
49
+ utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
54
50
  }
55
51
  }
56
- table[i] = crc >>> 0;
52
+ return utf8CharCodes;
57
53
  }
58
- return table;
59
- }
60
54
 
61
- function getCrcTable() {
62
- if (!cachedCrcTable) {
63
- cachedCrcTable = buildCRCTable();
55
+ var cachedCrcTable = null;
56
+
57
+ function buildCRCTable() {
58
+ var table = [];
59
+ for (var i = 0, j, crc; i < 256; ++i) {
60
+ crc = i;
61
+ j = 8;
62
+ while (j--) {
63
+ if ((crc & 1) == 1) {
64
+ crc = (crc >>> 1) ^ 0xEDB88320;
65
+ } else {
66
+ crc >>>= 1;
67
+ }
68
+ }
69
+ table[i] = crc >>> 0;
70
+ }
71
+ return table;
64
72
  }
65
- return cachedCrcTable;
66
- }
67
73
 
68
- return function(str) {
69
- var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
70
- for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
71
- y = (crc ^ utf8CharCodes[i]) & 0xFF;
72
- crc = (crc >>> 8) ^ crcTable[y];
74
+ function getCrcTable() {
75
+ if (!cachedCrcTable) {
76
+ cachedCrcTable = buildCRCTable();
77
+ }
78
+ return cachedCrcTable;
73
79
  }
74
- return (crc ^ -1) >>> 0;
75
- };
76
- })();
77
80
 
78
- var dom = api.dom;
81
+ return function(str) {
82
+ var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
83
+ for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
84
+ y = (crc ^ utf8CharCodes[i]) & 0xFF;
85
+ crc = (crc >>> 8) ^ crcTable[y];
86
+ }
87
+ return (crc ^ -1) >>> 0;
88
+ };
89
+ })();
79
90
 
80
- function escapeTextForHtml(str) {
81
- return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
82
- }
91
+ var dom = api.dom;
83
92
 
84
- function nodeToInfoString(node, infoParts) {
85
- infoParts = infoParts || [];
86
- var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
87
- var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
88
- var start = "", end = "";
89
- switch (nodeType) {
90
- case 3: // Text node
91
- start = escapeTextForHtml(node.nodeValue);
92
- break;
93
- case 8: // Comment
94
- start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";
95
- break;
96
- default:
97
- start = "<" + nodeInfo + ">";
98
- end = "</>";
99
- break;
100
- }
101
- if (start) {
102
- infoParts.push(start);
103
- }
104
- for (var i = 0; i < childCount; ++i) {
105
- nodeToInfoString(children[i], infoParts);
93
+ function escapeTextForHtml(str) {
94
+ return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
106
95
  }
107
- if (end) {
108
- infoParts.push(end);
109
- }
110
- return infoParts;
111
- }
112
-
113
- // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
114
- // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
115
- // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
116
- // innerHTML whenever the user changes an input within the element.
117
- function getElementChecksum(el) {
118
- var info = nodeToInfoString(el).join("");
119
- return crc32(info).toString(16);
120
- }
121
96
 
122
- function serializePosition(node, offset, rootNode) {
123
- var pathParts = [], n = node;
124
- rootNode = rootNode || dom.getDocument(node).documentElement;
125
- while (n && n != rootNode) {
126
- pathParts.push(dom.getNodeIndex(n, true));
127
- n = n.parentNode;
97
+ function nodeToInfoString(node, infoParts) {
98
+ infoParts = infoParts || [];
99
+ var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
100
+ var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
101
+ var start = "", end = "";
102
+ switch (nodeType) {
103
+ case 3: // Text node
104
+ start = escapeTextForHtml(node.nodeValue);
105
+ break;
106
+ case 8: // Comment
107
+ start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";
108
+ break;
109
+ default:
110
+ start = "<" + nodeInfo + ">";
111
+ end = "</>";
112
+ break;
113
+ }
114
+ if (start) {
115
+ infoParts.push(start);
116
+ }
117
+ for (var i = 0; i < childCount; ++i) {
118
+ nodeToInfoString(children[i], infoParts);
119
+ }
120
+ if (end) {
121
+ infoParts.push(end);
122
+ }
123
+ return infoParts;
128
124
  }
129
- return pathParts.join("/") + ":" + offset;
130
- }
131
125
 
132
- function deserializePosition(serialized, rootNode, doc) {
133
- if (!rootNode) {
134
- rootNode = (doc || document).documentElement;
126
+ // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
127
+ // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
128
+ // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
129
+ // innerHTML whenever the user changes an input within the element.
130
+ function getElementChecksum(el) {
131
+ var info = nodeToInfoString(el).join("");
132
+ return crc32(info).toString(16);
135
133
  }
136
- var parts = serialized.split(":");
137
- var node = rootNode;
138
- var nodeIndices = parts[0] ? parts[0].split("/") : [], i = nodeIndices.length, nodeIndex;
139
-
140
- while (i--) {
141
- nodeIndex = parseInt(nodeIndices[i], 10);
142
- if (nodeIndex < node.childNodes.length) {
143
- node = node.childNodes[nodeIndex];
144
- } else {
145
- throw module.createError("deserializePosition() failed: node " + dom.inspectNode(node) +
146
- " has no child with index " + nodeIndex + ", " + i);
134
+
135
+ function serializePosition(node, offset, rootNode) {
136
+ var pathParts = [], n = node;
137
+ rootNode = rootNode || dom.getDocument(node).documentElement;
138
+ while (n && n != rootNode) {
139
+ pathParts.push(dom.getNodeIndex(n, true));
140
+ n = n.parentNode;
147
141
  }
142
+ return pathParts.join("/") + ":" + offset;
148
143
  }
149
144
 
150
- return new dom.DomPosition(node, parseInt(parts[1], 10));
151
- }
145
+ function deserializePosition(serialized, rootNode, doc) {
146
+ if (!rootNode) {
147
+ rootNode = (doc || document).documentElement;
148
+ }
149
+ var parts = serialized.split(":");
150
+ var node = rootNode;
151
+ var nodeIndices = parts[0] ? parts[0].split("/") : [], i = nodeIndices.length, nodeIndex;
152
+
153
+ while (i--) {
154
+ nodeIndex = parseInt(nodeIndices[i], 10);
155
+ if (nodeIndex < node.childNodes.length) {
156
+ node = node.childNodes[nodeIndex];
157
+ } else {
158
+ throw module.createError("deserializePosition() failed: node " + dom.inspectNode(node) +
159
+ " has no child with index " + nodeIndex + ", " + i);
160
+ }
161
+ }
152
162
 
153
- function serializeRange(range, omitChecksum, rootNode) {
154
- rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
155
- if (!dom.isOrIsAncestorOf(rootNode, range.commonAncestorContainer)) {
156
- throw module.createError("serializeRange(): range " + range.inspect() +
157
- " is not wholly contained within specified root node " + dom.inspectNode(rootNode));
163
+ return new dom.DomPosition(node, parseInt(parts[1], 10));
158
164
  }
159
- var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
160
- serializePosition(range.endContainer, range.endOffset, rootNode);
161
- if (!omitChecksum) {
162
- serialized += "{" + getElementChecksum(rootNode) + "}";
163
- }
164
- return serialized;
165
- }
166
165
 
167
- var deserializeRegex = /^([^,]+),([^,\{]+)(\{([^}]+)\})?$/;
168
-
169
- function deserializeRange(serialized, rootNode, doc) {
170
- if (rootNode) {
171
- doc = doc || dom.getDocument(rootNode);
172
- } else {
173
- doc = doc || document;
174
- rootNode = doc.documentElement;
175
- }
176
- var result = deserializeRegex.exec(serialized);
177
- var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);
178
- if (checksum && checksum !== getElementChecksum(rootNode)) {
179
- throw module.createError("deserializeRange(): checksums of serialized range root node (" + checksum +
180
- ") and target root node (" + rootNodeChecksum + ") do not match");
166
+ function serializeRange(range, omitChecksum, rootNode) {
167
+ rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
168
+ if (!dom.isOrIsAncestorOf(rootNode, range.commonAncestorContainer)) {
169
+ throw module.createError("serializeRange(): range " + range.inspect() +
170
+ " is not wholly contained within specified root node " + dom.inspectNode(rootNode));
171
+ }
172
+ var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
173
+ serializePosition(range.endContainer, range.endOffset, rootNode);
174
+ if (!omitChecksum) {
175
+ serialized += "{" + getElementChecksum(rootNode) + "}";
176
+ }
177
+ return serialized;
181
178
  }
182
- var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
183
- var range = api.createRange(doc);
184
- range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
185
- return range;
186
- }
187
179
 
188
- function canDeserializeRange(serialized, rootNode, doc) {
189
- if (!rootNode) {
190
- rootNode = (doc || document).documentElement;
191
- }
192
- var result = deserializeRegex.exec(serialized);
193
- var checksum = result[3];
194
- return !checksum || checksum === getElementChecksum(rootNode);
195
- }
180
+ var deserializeRegex = /^([^,]+),([^,\{]+)(\{([^}]+)\})?$/;
196
181
 
197
- function serializeSelection(selection, omitChecksum, rootNode) {
198
- selection = api.getSelection(selection);
199
- var ranges = selection.getAllRanges(), serializedRanges = [];
200
- for (var i = 0, len = ranges.length; i < len; ++i) {
201
- serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
182
+ function deserializeRange(serialized, rootNode, doc) {
183
+ if (rootNode) {
184
+ doc = doc || dom.getDocument(rootNode);
185
+ } else {
186
+ doc = doc || document;
187
+ rootNode = doc.documentElement;
188
+ }
189
+ var result = deserializeRegex.exec(serialized);
190
+ var checksum = result[4];
191
+ if (checksum) {
192
+ var rootNodeChecksum = getElementChecksum(rootNode);
193
+ if (checksum !== rootNodeChecksum) {
194
+ throw module.createError("deserializeRange(): checksums of serialized range root node (" + checksum +
195
+ ") and target root node (" + rootNodeChecksum + ") do not match");
196
+ }
197
+ }
198
+ var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
199
+ var range = api.createRange(doc);
200
+ range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
201
+ return range;
202
202
  }
203
- return serializedRanges.join("|");
204
- }
205
203
 
206
- function deserializeSelection(serialized, rootNode, win) {
207
- if (rootNode) {
208
- win = win || dom.getWindow(rootNode);
209
- } else {
210
- win = win || window;
211
- rootNode = win.document.documentElement;
204
+ function canDeserializeRange(serialized, rootNode, doc) {
205
+ if (!rootNode) {
206
+ rootNode = (doc || document).documentElement;
207
+ }
208
+ var result = deserializeRegex.exec(serialized);
209
+ var checksum = result[3];
210
+ return !checksum || checksum === getElementChecksum(rootNode);
212
211
  }
213
- var serializedRanges = serialized.split("|");
214
- var sel = api.getSelection(win);
215
- var ranges = [];
216
212
 
217
- for (var i = 0, len = serializedRanges.length; i < len; ++i) {
218
- ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
213
+ function serializeSelection(selection, omitChecksum, rootNode) {
214
+ selection = api.getSelection(selection);
215
+ var ranges = selection.getAllRanges(), serializedRanges = [];
216
+ for (var i = 0, len = ranges.length; i < len; ++i) {
217
+ serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
218
+ }
219
+ return serializedRanges.join("|");
219
220
  }
220
- sel.setRanges(ranges);
221
-
222
- return sel;
223
- }
224
221
 
225
- function canDeserializeSelection(serialized, rootNode, win) {
226
- var doc;
227
- if (rootNode) {
228
- doc = win ? win.document : dom.getDocument(rootNode);
229
- } else {
230
- win = win || window;
231
- rootNode = win.document.documentElement;
232
- }
233
- var serializedRanges = serialized.split("|");
222
+ function deserializeSelection(serialized, rootNode, win) {
223
+ if (rootNode) {
224
+ win = win || dom.getWindow(rootNode);
225
+ } else {
226
+ win = win || window;
227
+ rootNode = win.document.documentElement;
228
+ }
229
+ var serializedRanges = serialized.split("|");
230
+ var sel = api.getSelection(win);
231
+ var ranges = [];
234
232
 
235
- for (var i = 0, len = serializedRanges.length; i < len; ++i) {
236
- if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
237
- return false;
233
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
234
+ ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
238
235
  }
239
- }
236
+ sel.setRanges(ranges);
240
237
 
241
- return true;
242
- }
238
+ return sel;
239
+ }
243
240
 
244
- var cookieName = "rangySerializedSelection";
241
+ function canDeserializeSelection(serialized, rootNode, win) {
242
+ var doc;
243
+ if (rootNode) {
244
+ doc = win ? win.document : dom.getDocument(rootNode);
245
+ } else {
246
+ win = win || window;
247
+ rootNode = win.document.documentElement;
248
+ }
249
+ var serializedRanges = serialized.split("|");
245
250
 
246
- function getSerializedSelectionFromCookie(cookie) {
247
- var parts = cookie.split(/[;,]/);
248
- for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
249
- nameVal = parts[i].split("=");
250
- if (nameVal[0].replace(/^\s+/, "") == cookieName) {
251
- val = nameVal[1];
252
- if (val) {
253
- return decodeURIComponent(val.replace(/\s+$/, ""));
251
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
252
+ if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
253
+ return false;
254
254
  }
255
255
  }
256
- }
257
- return null;
258
- }
259
256
 
260
- function restoreSelectionFromCookie(win) {
261
- win = win || window;
262
- var serialized = getSerializedSelectionFromCookie(win.document.cookie);
263
- if (serialized) {
264
- deserializeSelection(serialized, win.doc);
257
+ return true;
265
258
  }
266
- }
267
-
268
- function saveSelectionCookie(win, props) {
269
- win = win || window;
270
- props = (typeof props == "object") ? props : {};
271
- var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
272
- var path = props.path ? ";path=" + props.path : "";
273
- var domain = props.domain ? ";domain=" + props.domain : "";
274
- var secure = props.secure ? ";secure" : "";
275
- var serialized = serializeSelection(api.getSelection(win));
276
- win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
277
- }
278
259
 
279
- api.serializePosition = serializePosition;
280
- api.deserializePosition = deserializePosition;
260
+ var cookieName = "rangySerializedSelection";
281
261
 
282
- api.serializeRange = serializeRange;
283
- api.deserializeRange = deserializeRange;
284
- api.canDeserializeRange = canDeserializeRange;
262
+ function getSerializedSelectionFromCookie(cookie) {
263
+ var parts = cookie.split(/[;,]/);
264
+ for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
265
+ nameVal = parts[i].split("=");
266
+ if (nameVal[0].replace(/^\s+/, "") == cookieName) {
267
+ val = nameVal[1];
268
+ if (val) {
269
+ return decodeURIComponent(val.replace(/\s+$/, ""));
270
+ }
271
+ }
272
+ }
273
+ return null;
274
+ }
285
275
 
286
- api.serializeSelection = serializeSelection;
287
- api.deserializeSelection = deserializeSelection;
288
- api.canDeserializeSelection = canDeserializeSelection;
276
+ function restoreSelectionFromCookie(win) {
277
+ win = win || window;
278
+ var serialized = getSerializedSelectionFromCookie(win.document.cookie);
279
+ if (serialized) {
280
+ deserializeSelection(serialized, win.doc);
281
+ }
282
+ }
289
283
 
290
- api.restoreSelectionFromCookie = restoreSelectionFromCookie;
291
- api.saveSelectionCookie = saveSelectionCookie;
284
+ function saveSelectionCookie(win, props) {
285
+ win = win || window;
286
+ props = (typeof props == "object") ? props : {};
287
+ var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
288
+ var path = props.path ? ";path=" + props.path : "";
289
+ var domain = props.domain ? ";domain=" + props.domain : "";
290
+ var secure = props.secure ? ";secure" : "";
291
+ var serialized = serializeSelection(api.getSelection(win));
292
+ win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
293
+ }
292
294
 
293
- api.getElementChecksum = getElementChecksum;
294
- api.nodeToInfoString = nodeToInfoString;
295
- });
295
+ util.extend(api, {
296
+ serializePosition: serializePosition,
297
+ deserializePosition: deserializePosition,
298
+ serializeRange: serializeRange,
299
+ deserializeRange: deserializeRange,
300
+ canDeserializeRange: canDeserializeRange,
301
+ serializeSelection: serializeSelection,
302
+ deserializeSelection: deserializeSelection,
303
+ canDeserializeSelection: canDeserializeSelection,
304
+ restoreSelectionFromCookie: restoreSelectionFromCookie,
305
+ saveSelectionCookie: saveSelectionCookie,
306
+ getElementChecksum: getElementChecksum,
307
+ nodeToInfoString: nodeToInfoString
308
+ });
309
+
310
+ util.crc32 = crc32;
311
+ });
312
+
313
+ return rangy;
314
+ }, this);