rangy-rails 1.3alpha.780.0 → 1.3alpha.804.0

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.
@@ -9,7 +9,227 @@
9
9
  *
10
10
  * Copyright 2013, Tim Down
11
11
  * Licensed under the MIT license.
12
- * Version: 1.3alpha.780M
13
- * Build date: 17 May 2013
12
+ * Version: 1.3alpha.804
13
+ * Build date: 8 December 2013
14
14
  */
15
- rangy.createModule("SaveRestore",["WrappedRange"],function(a,b){function e(a,b){return(b||document).getElementById(a)}function f(a,b){var e="selectionBoundary_"+ +(new Date)+"_"+(""+Math.random()).slice(2),f,g=c.getDocument(a.startContainer),h=a.cloneRange();return h.collapse(b),f=g.createElement("span"),f.id=e,f.style.lineHeight="0",f.style.display="none",f.className="rangySelectionBoundary",f.appendChild(g.createTextNode(d)),h.insertNode(f),h.detach(),f}function g(a,c,d,f){var g=e(d,a);g?(c[f?"setStartBefore":"setEndBefore"](g),g.parentNode.removeChild(g)):b.warn("Marker element has been removed. Cannot restore selection.")}function h(a,b){return b.compareBoundaryPoints(a.START_TO_START,a)}function i(b,c){var d,e,g=a.DomRange.getRangeDocument(b),h=b.toString();return b.collapsed?(e=f(b,!1),{document:g,markerId:e.id,collapsed:!0}):(e=f(b,!1),d=f(b,!0),{document:g,startMarkerId:d.id,endMarkerId:e.id,collapsed:!1,backward:c,toString:function(){return"original text: '"+h+"', new text: '"+b.toString()+"'"}})}function j(c,d){var f=c.document;typeof d=="undefined"&&(d=!0);var h=a.createRange(f);if(c.collapsed){var i=e(c.markerId,f);if(i){i.style.display="inline";var j=i.previousSibling;j&&j.nodeType==3?(i.parentNode.removeChild(i),h.collapseToPoint(j,j.length)):(h.collapseBefore(i),i.parentNode.removeChild(i))}else b.warn("Marker element has been removed. Cannot restore selection.")}else g(f,h,c.startMarkerId,!0),g(f,h,c.endMarkerId,!1);return d&&h.normalizeBoundaries(),h}function k(b,c){var d=[],f,g;b=b.slice(0),b.sort(h);for(var j=0,k=b.length;j<k;++j)d[j]=i(b[j],c);for(j=k-1;j>=0;--j)f=b[j],g=a.DomRange.getRangeDocument(f),f.collapsed?f.collapseAfter(e(d[j].markerId,g)):(f.setEndBefore(e(d[j].endMarkerId,g)),f.setStartAfter(e(d[j].startMarkerId,g)));return d}function l(c){if(!a.isSelectionValid(c))return b.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus."),null;var d=a.getSelection(c),e=d.getAllRanges(),f=e.length==1&&d.isBackward(),g=k(e,f);return f?d.setSingleRange(e[0],"backward"):d.setRanges(e),{win:c,rangeInfos:g,restored:!1}}function m(a){var b=[],c=a.length;for(var d=c-1;d>=0;d--)b[d]=j(a[d],!0);return b}function n(b,c){if(!b.restored){var d=b.rangeInfos,e=a.getSelection(b.win),f=m(d),g=d.length;g==1&&c&&a.features.selectionHasExtend&&d[0].backward?(e.removeAllRanges(),e.addRange(f[0],!0)):e.setRanges(f),b.restored=!0}}function o(a,b){var c=e(b,a);c&&c.parentNode.removeChild(c)}function p(a){var b=a.rangeInfos;for(var c=0,d=b.length,e;c<d;++c)e=b[c],e.collapsed?o(a.doc,e.markerId):(o(a.doc,e.startMarkerId),o(a.doc,e.endMarkerId))}var c=a.dom,d="\ufeff";a.util.extend(a,{saveRange:i,restoreRange:j,saveRanges:k,restoreRanges:m,saveSelection:l,restoreSelection:n,removeMarkerElement:o,removeMarkers:p})})
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);
22
+ }
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.");
53
+ }
54
+ }
55
+
56
+ function compareRanges(r1, r2) {
57
+ return r2.compareBoundaryPoints(r1.START_TO_START, r1);
58
+ }
59
+
60
+ function saveRange(range, backward) {
61
+ var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
62
+
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);
73
+
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
+ };
84
+ }
85
+ }
86
+
87
+ function restoreRange(rangeInfo, normalize) {
88
+ var doc = rangeInfo.document;
89
+ if (typeof normalize == "undefined") {
90
+ normalize = true;
91
+ }
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
+ // Workaround for issue 17
100
+ if (previousNode && previousNode.nodeType == 3) {
101
+ markerEl.parentNode.removeChild(markerEl);
102
+ range.collapseToPoint(previousNode, previousNode.length);
103
+ } else {
104
+ range.collapseBefore(markerEl);
105
+ markerEl.parentNode.removeChild(markerEl);
106
+ }
107
+ } else {
108
+ module.warn("Marker element has been removed. Cannot restore selection.");
109
+ }
110
+ } else {
111
+ setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
112
+ setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
113
+ }
114
+
115
+ if (normalize) {
116
+ range.normalizeBoundaries();
117
+ }
118
+
119
+ return range;
120
+ }
121
+
122
+ function saveRanges(ranges, backward) {
123
+ var rangeInfos = [], range, doc;
124
+
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);
128
+
129
+ for (var i = 0, len = ranges.length; i < len; ++i) {
130
+ rangeInfos[i] = saveRange(ranges[i], backward);
131
+ }
132
+
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));
143
+ }
144
+ }
145
+
146
+ return rangeInfos;
147
+ }
148
+
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());
157
+
158
+ var rangeInfos = saveRanges(ranges, backward);
159
+
160
+ // Ensure current selection is unaffected
161
+ if (backward) {
162
+ sel.setSingleRange(ranges[0], "backward");
163
+ } else {
164
+ sel.setRanges(ranges);
165
+ }
166
+
167
+ return {
168
+ win: win,
169
+ rangeInfos: rangeInfos,
170
+ restored: false
171
+ };
172
+ }
173
+
174
+ function restoreRanges(rangeInfos) {
175
+ var ranges = [];
176
+
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;
180
+
181
+ for (var i = rangeCount - 1; i >= 0; i--) {
182
+ ranges[i] = restoreRange(rangeInfos[i], true);
183
+ }
184
+
185
+ return ranges;
186
+ }
187
+
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;
193
+
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);
199
+ }
200
+
201
+ savedSelection.restored = true;
202
+ }
203
+ }
204
+
205
+ function removeMarkerElement(doc, markerId) {
206
+ var markerEl = gEBI(markerId, doc);
207
+ if (markerEl) {
208
+ markerEl.parentNode.removeChild(markerEl);
209
+ }
210
+ }
211
+
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);
221
+ }
222
+ }
223
+ }
224
+
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
234
+ });
235
+ });
@@ -10,7 +10,286 @@
10
10
  *
11
11
  * Copyright 2013, Tim Down
12
12
  * Licensed under the MIT license.
13
- * Version: 1.3alpha.780M
14
- * Build date: 17 May 2013
13
+ * Version: 1.3alpha.804
14
+ * Build date: 8 December 2013
15
15
  */
16
- rangy.createModule("Serializer",["WrappedSelection"],function(a,b){function f(a){return a.replace(/</g,"&lt;").replace(/>/g,"&gt;")}function g(a,b){b=b||[];var c=a.nodeType,d=a.childNodes,e=d.length,h=[c,a.nodeName,e].join(":"),i="",j="";switch(c){case 3:i=f(a.nodeValue);break;case 8:i="<!--"+f(a.nodeValue)+"-->";break;default:i="<"+h+">",j="</>"}i&&b.push(i);for(var k=0;k<e;++k)g(d[k],b);return j&&b.push(j),b}function h(a){var b=g(a).join("");return d(b).toString(16)}function i(a,b,c){var d=[],f=a;c=c||e.getDocument(a).documentElement;while(f&&f!=c)d.push(e.getNodeIndex(f,!0)),f=f.parentNode;return d.join("/")+":"+b}function j(a,c,d){c||(c=(d||document).documentElement);var f=a.split(":"),g=c,h=f[0]?f[0].split("/"):[],i=h.length,j;while(i--){j=parseInt(h[i],10);if(!(j<g.childNodes.length))throw b.createError("deserializePosition() failed: node "+e.inspectNode(g)+" has no child with index "+j+", "+i);g=g.childNodes[j]}return new e.DomPosition(g,parseInt(f[1],10))}function k(c,d,f){f=f||a.DomRange.getRangeDocument(c).documentElement;if(!e.isOrIsAncestorOf(f,c.commonAncestorContainer))throw b.createError("serializeRange(): range "+c.inspect()+" is not wholly contained within specified root node "+e.inspectNode(f));var g=i(c.startContainer,c.startOffset,f)+","+i(c.endContainer,c.endOffset,f);return d||(g+="{"+h(f)+"}"),g}function l(c,d,f){d?f=f||e.getDocument(d):(f=f||document,d=f.documentElement);var g=/^([^,]+),([^,\{]+)(\{([^}]+)\})?$/.exec(c),i=g[4],k=h(d);if(i&&i!==h(d))throw b.createError("deserializeRange(): checksums of serialized range root node ("+i+") and target root node ("+k+") do not match");var l=j(g[1],d,f),m=j(g[2],d,f),n=a.createRange(f);return n.setStartAndEnd(l.node,l.offset,m.node,m.offset),n}function m(a,b,c){b||(b=(c||document).documentElement);var d=/^([^,]+),([^,]+)(\{([^}]+)\})?$/.exec(a),e=d[3];return!e||e===h(b)}function n(b,c,d){b=a.getSelection(b);var e=b.getAllRanges(),f=[];for(var g=0,h=e.length;g<h;++g)f[g]=k(e[g],c,d);return f.join("|")}function o(b,c,d){c?d=d||e.getWindow(c):(d=d||window,c=d.document.documentElement);var f=b.split("|"),g=a.getSelection(d),h=[];for(var i=0,j=f.length;i<j;++i)h[i]=l(f[i],c,d.document);return g.setRanges(h),g}function p(a,b,c){var d;b?d=c?c.document:e.getDocument(b):(c=c||window,b=c.document.documentElement);var f=a.split("|");for(var g=0,h=f.length;g<h;++g)if(!m(f[g],b,d))return!1;return!0}function r(a){var b=a.split(/[;,]/);for(var c=0,d=b.length,e,f;c<d;++c){e=b[c].split("=");if(e[0].replace(/^\s+/,"")==q){f=e[1];if(f)return decodeURIComponent(f.replace(/\s+$/,""))}}return null}function s(a){a=a||window;var b=r(a.document.cookie);b&&o(b,a.doc)}function t(b,c){b=b||window,c=typeof c=="object"?c:{};var d=c.expires?";expires="+c.expires.toUTCString():"",e=c.path?";path="+c.path:"",f=c.domain?";domain="+c.domain:"",g=c.secure?";secure":"",h=n(a.getSelection(b));b.document.cookie=encodeURIComponent(q)+"="+encodeURIComponent(h)+d+e+f+g}var c="undefined";(typeof encodeURIComponent==c||typeof decodeURIComponent==c)&&b.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");var d=function(){function a(a){var b=[];for(var c=0,d=a.length,e;c<d;++c)e=a.charCodeAt(c),e<128?b.push(e):e<2048?b.push(e>>6|192,e&63|128):b.push(e>>12|224,e>>6&63|128,e&63|128);return b}function c(){var a=[];for(var b=0,c,d;b<256;++b){d=b,c=8;while(c--)(d&1)==1?d=d>>>1^3988292384:d>>>=1;a[b]=d>>>0}return a}function d(){return b||(b=c()),b}var b=null;return function(b){var c=a(b),e=-1,f=d();for(var g=0,h=c.length,i;g<h;++g)i=(e^c[g])&255,e=e>>>8^f[i];return(e^-1)>>>0}}(),e=a.dom,q="rangySerializedSelection";a.serializePosition=i,a.deserializePosition=j,a.serializeRange=k,a.deserializeRange=l,a.canDeserializeRange=m,a.serializeSelection=n,a.deserializeSelection=o,a.canDeserializeSelection=p,a.restoreSelectionFromCookie=s,a.saveSelectionCookie=t,a.getElementChecksum=h,a.nodeToInfoString=g})
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");
22
+ }
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;
40
+ }
41
+
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;
52
+ } else {
53
+ crc >>>= 1;
54
+ }
55
+ }
56
+ table[i] = crc >>> 0;
57
+ }
58
+ return table;
59
+ }
60
+
61
+ function getCrcTable() {
62
+ if (!cachedCrcTable) {
63
+ cachedCrcTable = buildCRCTable();
64
+ }
65
+ return cachedCrcTable;
66
+ }
67
+
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];
73
+ }
74
+ return (crc ^ -1) >>> 0;
75
+ };
76
+ })();
77
+
78
+ var dom = api.dom;
79
+
80
+ function escapeTextForHtml(str) {
81
+ return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
82
+ }
83
+
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);
106
+ }
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
+
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;
128
+ }
129
+ return pathParts.join("/") + ":" + offset;
130
+ }
131
+
132
+ function deserializePosition(serialized, rootNode, doc) {
133
+ if (!rootNode) {
134
+ rootNode = (doc || document).documentElement;
135
+ }
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);
147
+ }
148
+ }
149
+
150
+ return new dom.DomPosition(node, parseInt(parts[1], 10));
151
+ }
152
+
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));
158
+ }
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
+
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");
181
+ }
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
+
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
+ }
196
+
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);
202
+ }
203
+ return serializedRanges.join("|");
204
+ }
205
+
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;
212
+ }
213
+ var serializedRanges = serialized.split("|");
214
+ var sel = api.getSelection(win);
215
+ var ranges = [];
216
+
217
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
218
+ ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
219
+ }
220
+ sel.setRanges(ranges);
221
+
222
+ return sel;
223
+ }
224
+
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("|");
234
+
235
+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
236
+ if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
237
+ return false;
238
+ }
239
+ }
240
+
241
+ return true;
242
+ }
243
+
244
+ var cookieName = "rangySerializedSelection";
245
+
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+$/, ""));
254
+ }
255
+ }
256
+ }
257
+ return null;
258
+ }
259
+
260
+ function restoreSelectionFromCookie(win) {
261
+ win = win || window;
262
+ var serialized = getSerializedSelectionFromCookie(win.document.cookie);
263
+ if (serialized) {
264
+ deserializeSelection(serialized, win.doc);
265
+ }
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
+
279
+ api.serializePosition = serializePosition;
280
+ api.deserializePosition = deserializePosition;
281
+
282
+ api.serializeRange = serializeRange;
283
+ api.deserializeRange = deserializeRange;
284
+ api.canDeserializeRange = canDeserializeRange;
285
+
286
+ api.serializeSelection = serializeSelection;
287
+ api.deserializeSelection = deserializeSelection;
288
+ api.canDeserializeSelection = canDeserializeSelection;
289
+
290
+ api.restoreSelectionFromCookie = restoreSelectionFromCookie;
291
+ api.saveSelectionCookie = saveSelectionCookie;
292
+
293
+ api.getElementChecksum = getElementChecksum;
294
+ api.nodeToInfoString = nodeToInfoString;
295
+ });