Added Android code
[wl-app.git] / Android / webViewMarker / src / main / assets / rangy-serializer.js
1 /**\r
2  * @license Serializer module for Rangy.\r
3  * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a\r
4  * cookie or local storage and restore it on the user's next visit to the same page.\r
5  *\r
6  * Part of Rangy, a cross-browser JavaScript range and selection library\r
7  * http://code.google.com/p/rangy/\r
8  *\r
9  * Depends on Rangy core.\r
10  *\r
11  * Copyright 2012, Tim Down\r
12  * Licensed under the MIT license.\r
13  * Version: 1.2.3\r
14  * Build date: 26 February 2012\r
15  */\r
16 rangy.createModule("Serializer", function(api, module) {\r
17     api.requireModules( ["WrappedSelection", "WrappedRange"] );\r
18     var UNDEF = "undefined";\r
19 \r
20     // encodeURIComponent and decodeURIComponent are required for cookie handling\r
21     if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {\r
22         module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");\r
23     }\r
24 \r
25     // Checksum for checking whether range can be serialized\r
26     var crc32 = (function() {\r
27         function utf8encode(str) {\r
28             var utf8CharCodes = [];\r
29 \r
30             for (var i = 0, len = str.length, c; i < len; ++i) {\r
31                 c = str.charCodeAt(i);\r
32                 if (c < 128) {\r
33                     utf8CharCodes.push(c);\r
34                 } else if (c < 2048) {\r
35                     utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);\r
36                 } else {\r
37                     utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);\r
38                 }\r
39             }\r
40             return utf8CharCodes;\r
41         }\r
42 \r
43         var cachedCrcTable = null;\r
44 \r
45         function buildCRCTable() {\r
46             var table = [];\r
47             for (var i = 0, j, crc; i < 256; ++i) {\r
48                 crc = i;\r
49                 j = 8;\r
50                 while (j--) {\r
51                     if ((crc & 1) == 1) {\r
52                         crc = (crc >>> 1) ^ 0xEDB88320;\r
53                     } else {\r
54                         crc >>>= 1;\r
55                     }\r
56                 }\r
57                 table[i] = crc >>> 0;\r
58             }\r
59             return table;\r
60         }\r
61 \r
62         function getCrcTable() {\r
63             if (!cachedCrcTable) {\r
64                 cachedCrcTable = buildCRCTable();\r
65             }\r
66             return cachedCrcTable;\r
67         }\r
68 \r
69         return function(str) {\r
70             var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();\r
71             for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {\r
72                 y = (crc ^ utf8CharCodes[i]) & 0xFF;\r
73                 crc = (crc >>> 8) ^ crcTable[y];\r
74             }\r
75             return (crc ^ -1) >>> 0;\r
76         };\r
77     })();\r
78 \r
79     var dom = api.dom;\r
80 \r
81     function escapeTextForHtml(str) {\r
82         return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");\r
83     }\r
84 \r
85     function nodeToInfoString(node, infoParts) {\r
86         infoParts = infoParts || [];\r
87         var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;\r
88         var nodeInfo = [nodeType, node.nodeName, childCount].join(":");\r
89         var start = "", end = "";\r
90         switch (nodeType) {\r
91             case 3: // Text node\r
92                 start = escapeTextForHtml(node.nodeValue);\r
93                 break;\r
94             case 8: // Comment\r
95                 start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";\r
96                 break;\r
97             default:\r
98                 start = "<" + nodeInfo + ">";\r
99                 end = "</>";\r
100                 break;\r
101         }\r
102         if (start) {\r
103             infoParts.push(start);\r
104         }\r
105         for (var i = 0; i < childCount; ++i) {\r
106             nodeToInfoString(children[i], infoParts);\r
107         }\r
108         if (end) {\r
109             infoParts.push(end);\r
110         }\r
111         return infoParts;\r
112     }\r
113 \r
114     // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all\r
115     // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around\r
116     // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's\r
117     // innerHTML whenever the user changes an input within the element.\r
118     function getElementChecksum(el) {\r
119         var info = nodeToInfoString(el).join("");\r
120         return crc32(info).toString(16);\r
121     }\r
122 \r
123     function serializePosition(node, offset, rootNode) {\r
124         var pathBits = [], n = node;\r
125         rootNode = rootNode || dom.getDocument(node).documentElement;\r
126         while (n && n != rootNode) {\r
127             pathBits.push(dom.getNodeIndex(n, true));\r
128             n = n.parentNode;\r
129         }\r
130         return pathBits.join("/") + ":" + offset;\r
131     }\r
132 \r
133     function deserializePosition(serialized, rootNode, doc) {\r
134         if (rootNode) {\r
135             doc = doc || dom.getDocument(rootNode);\r
136         } else {\r
137             doc = doc || document;\r
138             rootNode = doc.documentElement;\r
139         }\r
140         var bits = serialized.split(":");\r
141         var node = rootNode;\r
142         var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex;\r
143 \r
144         while (i--) {\r
145             nodeIndex = parseInt(nodeIndices[i], 10);\r
146             if (nodeIndex < node.childNodes.length) {\r
147                 node = node.childNodes[parseInt(nodeIndices[i], 10)];\r
148             } else {\r
149                 throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) +\r
150                         " has no child with index " + nodeIndex + ", " + i);\r
151             }\r
152         }\r
153 \r
154         return new dom.DomPosition(node, parseInt(bits[1], 10));\r
155     }\r
156 \r
157     function serializeRange(range, omitChecksum, rootNode) {\r
158         rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;\r
159         if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) {\r
160             throw new Error("serializeRange: range is not wholly contained within specified root node");\r
161         }\r
162         var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +\r
163             serializePosition(range.endContainer, range.endOffset, rootNode);\r
164         if (!omitChecksum) {\r
165             serialized += "{" + getElementChecksum(rootNode) + "}";\r
166         }\r
167         return serialized;\r
168     }\r
169 \r
170     function deserializeRange(serialized, rootNode, doc) {\r
171         if (rootNode) {\r
172             doc = doc || dom.getDocument(rootNode);\r
173         } else {\r
174             doc = doc || document;\r
175             rootNode = doc.documentElement;\r
176         }\r
177         var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized);\r
178         var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);\r
179         if (checksum && checksum !== getElementChecksum(rootNode)) {\r
180             throw new Error("deserializeRange: checksums of serialized range root node (" + checksum +\r
181                     ") and target root node (" + rootNodeChecksum + ") do not match");\r
182         }\r
183         var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);\r
184         var range = api.createRange(doc);\r
185         range.setStart(start.node, start.offset);\r
186         range.setEnd(end.node, end.offset);\r
187         return range;\r
188     }\r
189 \r
190     function canDeserializeRange(serialized, rootNode, doc) {\r
191         if (rootNode) {\r
192             doc = doc || dom.getDocument(rootNode);\r
193         } else {\r
194             doc = doc || document;\r
195             rootNode = doc.documentElement;\r
196         }\r
197         var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized);\r
198         var checksum = result[3];\r
199         return !checksum || checksum === getElementChecksum(rootNode);\r
200     }\r
201 \r
202     function serializeSelection(selection, omitChecksum, rootNode) {\r
203         selection = selection || api.getSelection();\r
204         var ranges = selection.getAllRanges(), serializedRanges = [];\r
205         for (var i = 0, len = ranges.length; i < len; ++i) {\r
206             serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);\r
207         }\r
208         return serializedRanges.join("|");\r
209     }\r
210 \r
211     function deserializeSelection(serialized, rootNode, win) {\r
212         if (rootNode) {\r
213             win = win || dom.getWindow(rootNode);\r
214         } else {\r
215             win = win || window;\r
216             rootNode = win.document.documentElement;\r
217         }\r
218         var serializedRanges = serialized.split("|");\r
219         var sel = api.getSelection(win);\r
220         var ranges = [];\r
221 \r
222         for (var i = 0, len = serializedRanges.length; i < len; ++i) {\r
223             ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);\r
224         }\r
225         sel.setRanges(ranges);\r
226 \r
227         return sel;\r
228     }\r
229 \r
230     function canDeserializeSelection(serialized, rootNode, win) {\r
231         var doc;\r
232         if (rootNode) {\r
233             doc = win ? win.document : dom.getDocument(rootNode);\r
234         } else {\r
235             win = win || window;\r
236             rootNode = win.document.documentElement;\r
237         }\r
238         var serializedRanges = serialized.split("|");\r
239 \r
240         for (var i = 0, len = serializedRanges.length; i < len; ++i) {\r
241             if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {\r
242                 return false;\r
243             }\r
244         }\r
245 \r
246         return true;\r
247     }\r
248 \r
249 \r
250     var cookieName = "rangySerializedSelection";\r
251 \r
252     function getSerializedSelectionFromCookie(cookie) {\r
253         var parts = cookie.split(/[;,]/);\r
254         for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {\r
255             nameVal = parts[i].split("=");\r
256             if (nameVal[0].replace(/^\s+/, "") == cookieName) {\r
257                 val = nameVal[1];\r
258                 if (val) {\r
259                     return decodeURIComponent(val.replace(/\s+$/, ""));\r
260                 }\r
261             }\r
262         }\r
263         return null;\r
264     }\r
265 \r
266     function restoreSelectionFromCookie(win) {\r
267         win = win || window;\r
268         var serialized = getSerializedSelectionFromCookie(win.document.cookie);\r
269         if (serialized) {\r
270             deserializeSelection(serialized, win.doc)\r
271         }\r
272     }\r
273 \r
274     function saveSelectionCookie(win, props) {\r
275         win = win || window;\r
276         props = (typeof props == "object") ? props : {};\r
277         var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";\r
278         var path = props.path ? ";path=" + props.path : "";\r
279         var domain = props.domain ? ";domain=" + props.domain : "";\r
280         var secure = props.secure ? ";secure" : "";\r
281         var serialized = serializeSelection(api.getSelection(win));\r
282         win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;\r
283     }\r
284 \r
285     api.serializePosition = serializePosition;\r
286     api.deserializePosition = deserializePosition;\r
287 \r
288     api.serializeRange = serializeRange;\r
289     api.deserializeRange = deserializeRange;\r
290     api.canDeserializeRange = canDeserializeRange;\r
291 \r
292     api.serializeSelection = serializeSelection;\r
293     api.deserializeSelection = deserializeSelection;\r
294     api.canDeserializeSelection = canDeserializeSelection;\r
295 \r
296     api.restoreSelectionFromCookie = restoreSelectionFromCookie;\r
297     api.saveSelectionCookie = saveSelectionCookie;\r
298 \r
299     api.getElementChecksum = getElementChecksum;\r
300 });\r