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