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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ });