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