Added Android code
[wl-app.git] / Android / folioreader / src / main / assets / js / rangy-serializer.js
diff --git a/Android/folioreader/src/main/assets/js/rangy-serializer.js b/Android/folioreader/src/main/assets/js/rangy-serializer.js
new file mode 100755 (executable)
index 0000000..f2d5759
--- /dev/null
@@ -0,0 +1,314 @@
+/**
+ * Serializer module for Rangy.
+ * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
+ * cookie or local storage and restore it on the user's next visit to the same page.
+ *
+ * Part of Rangy, a cross-browser JavaScript range and selection library
+ * https://github.com/timdown/rangy
+ *
+ * Depends on Rangy core.
+ *
+ * Copyright 2015, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.3.0
+ * Build date: 10 May 2015
+ */
+(function(factory, root) {
+    if (typeof define == "function" && define.amd) {
+        // AMD. Register as an anonymous module with a dependency on Rangy.
+        define(["./rangy-core"], factory);
+    } else if (typeof module != "undefined" && typeof exports == "object") {
+        // Node/CommonJS style
+        module.exports = factory( require("rangy") );
+    } else {
+        // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
+        factory(root.rangy);
+    }
+})(function(rangy) {
+    rangy.createModule("Serializer", ["WrappedSelection"], function(api, module) {
+        var UNDEF = "undefined";
+        var util = api.util;
+
+        // encodeURIComponent and decodeURIComponent are required for cookie handling
+        if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
+            module.fail("encodeURIComponent and/or decodeURIComponent method is missing");
+        }
+
+        // Checksum for checking whether range can be serialized
+        var crc32 = (function() {
+            function utf8encode(str) {
+                var utf8CharCodes = [];
+
+                for (var i = 0, len = str.length, c; i < len; ++i) {
+                    c = str.charCodeAt(i);
+                    if (c < 128) {
+                        utf8CharCodes.push(c);
+                    } else if (c < 2048) {
+                        utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
+                    } else {
+                        utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
+                    }
+                }
+                return utf8CharCodes;
+            }
+
+            var cachedCrcTable = null;
+
+            function buildCRCTable() {
+                var table = [];
+                for (var i = 0, j, crc; i < 256; ++i) {
+                    crc = i;
+                    j = 8;
+                    while (j--) {
+                        if ((crc & 1) == 1) {
+                            crc = (crc >>> 1) ^ 0xEDB88320;
+                        } else {
+                            crc >>>= 1;
+                        }
+                    }
+                    table[i] = crc >>> 0;
+                }
+                return table;
+            }
+
+            function getCrcTable() {
+                if (!cachedCrcTable) {
+                    cachedCrcTable = buildCRCTable();
+                }
+                return cachedCrcTable;
+            }
+
+            return function(str) {
+                var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
+                for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
+                    y = (crc ^ utf8CharCodes[i]) & 0xFF;
+                    crc = (crc >>> 8) ^ crcTable[y];
+                }
+                return (crc ^ -1) >>> 0;
+            };
+        })();
+
+        var dom = api.dom;
+
+        function escapeTextForHtml(str) {
+            return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
+        }
+
+        function nodeToInfoString(node, infoParts) {
+            infoParts = infoParts || [];
+            var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
+            var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
+            var start = "", end = "";
+            switch (nodeType) {
+                case 3: // Text node
+                    start = escapeTextForHtml(node.nodeValue);
+                    break;
+                case 8: // Comment
+                    start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";
+                    break;
+                default:
+                    start = "<" + nodeInfo + ">";
+                    end = "</>";
+                    break;
+            }
+            if (start) {
+                infoParts.push(start);
+            }
+            for (var i = 0; i < childCount; ++i) {
+                nodeToInfoString(children[i], infoParts);
+            }
+            if (end) {
+                infoParts.push(end);
+            }
+            return infoParts;
+        }
+
+        // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
+        // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
+        // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
+        // innerHTML whenever the user changes an input within the element.
+        function getElementChecksum(el) {
+            var info = nodeToInfoString(el).join("");
+            return crc32(info).toString(16);
+        }
+
+        function serializePosition(node, offset, rootNode) {
+            var pathParts = [], n = node;
+            rootNode = rootNode || dom.getDocument(node).documentElement;
+            while (n && n != rootNode) {
+                pathParts.push(dom.getNodeIndex(n, true));
+                n = n.parentNode;
+            }
+            return pathParts.join("/") + ":" + offset;
+        }
+
+        function deserializePosition(serialized, rootNode, doc) {
+            if (!rootNode) {
+                rootNode = (doc || document).documentElement;
+            }
+            var parts = serialized.split(":");
+            var node = rootNode;
+            var nodeIndices = parts[0] ? parts[0].split("/") : [], i = nodeIndices.length, nodeIndex;
+
+            while (i--) {
+                nodeIndex = parseInt(nodeIndices[i], 10);
+                if (nodeIndex < node.childNodes.length) {
+                    node = node.childNodes[nodeIndex];
+                } else {
+                    throw module.createError("deserializePosition() failed: node " + dom.inspectNode(node) +
+                            " has no child with index " + nodeIndex + ", " + i);
+                }
+            }
+
+            return new dom.DomPosition(node, parseInt(parts[1], 10));
+        }
+
+        function serializeRange(range, omitChecksum, rootNode) {
+            rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
+            if (!dom.isOrIsAncestorOf(rootNode, range.commonAncestorContainer)) {
+                throw module.createError("serializeRange(): range " + range.inspect() +
+                    " is not wholly contained within specified root node " + dom.inspectNode(rootNode));
+            }
+            var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
+                serializePosition(range.endContainer, range.endOffset, rootNode);
+            if (!omitChecksum) {
+                serialized += "{" + getElementChecksum(rootNode) + "}";
+            }
+            return serialized;
+        }
+
+        var deserializeRegex = /^([^,]+),([^,\{]+)(\{([^}]+)\})?$/;
+
+        function deserializeRange(serialized, rootNode, doc) {
+            if (rootNode) {
+                doc = doc || dom.getDocument(rootNode);
+            } else {
+                doc = doc || document;
+                rootNode = doc.documentElement;
+            }
+            var result = deserializeRegex.exec(serialized);
+            var checksum = result[4];
+            if (checksum) {
+                var rootNodeChecksum = getElementChecksum(rootNode);
+                if (checksum !== rootNodeChecksum) {
+                    throw module.createError("deserializeRange(): checksums of serialized range root node (" + checksum +
+                        ") and target root node (" + rootNodeChecksum + ") do not match");
+                }
+            }
+            var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
+            var range = api.createRange(doc);
+            range.setStartAndEnd(start.node, start.offset, end.node, end.offset);
+            return range;
+        }
+
+        function canDeserializeRange(serialized, rootNode, doc) {
+            if (!rootNode) {
+                rootNode = (doc || document).documentElement;
+            }
+            var result = deserializeRegex.exec(serialized);
+            var checksum = result[3];
+            return !checksum || checksum === getElementChecksum(rootNode);
+        }
+
+        function serializeSelection(selection, omitChecksum, rootNode) {
+            selection = api.getSelection(selection);
+            var ranges = selection.getAllRanges(), serializedRanges = [];
+            for (var i = 0, len = ranges.length; i < len; ++i) {
+                serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
+            }
+            return serializedRanges.join("|");
+        }
+
+        function deserializeSelection(serialized, rootNode, win) {
+            if (rootNode) {
+                win = win || dom.getWindow(rootNode);
+            } else {
+                win = win || window;
+                rootNode = win.document.documentElement;
+            }
+            var serializedRanges = serialized.split("|");
+            var sel = api.getSelection(win);
+            var ranges = [];
+
+            for (var i = 0, len = serializedRanges.length; i < len; ++i) {
+                ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
+            }
+            sel.setRanges(ranges);
+
+            return sel;
+        }
+
+        function canDeserializeSelection(serialized, rootNode, win) {
+            var doc;
+            if (rootNode) {
+                doc = win ? win.document : dom.getDocument(rootNode);
+            } else {
+                win = win || window;
+                rootNode = win.document.documentElement;
+            }
+            var serializedRanges = serialized.split("|");
+
+            for (var i = 0, len = serializedRanges.length; i < len; ++i) {
+                if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        var cookieName = "rangySerializedSelection";
+
+        function getSerializedSelectionFromCookie(cookie) {
+            var parts = cookie.split(/[;,]/);
+            for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
+                nameVal = parts[i].split("=");
+                if (nameVal[0].replace(/^\s+/, "") == cookieName) {
+                    val = nameVal[1];
+                    if (val) {
+                        return decodeURIComponent(val.replace(/\s+$/, ""));
+                    }
+                }
+            }
+            return null;
+        }
+
+        function restoreSelectionFromCookie(win) {
+            win = win || window;
+            var serialized = getSerializedSelectionFromCookie(win.document.cookie);
+            if (serialized) {
+                deserializeSelection(serialized, win.doc);
+            }
+        }
+
+        function saveSelectionCookie(win, props) {
+            win = win || window;
+            props = (typeof props == "object") ? props : {};
+            var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
+            var path = props.path ? ";path=" + props.path : "";
+            var domain = props.domain ? ";domain=" + props.domain : "";
+            var secure = props.secure ? ";secure" : "";
+            var serialized = serializeSelection(api.getSelection(win));
+            win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
+        }
+
+        util.extend(api, {
+            serializePosition: serializePosition,
+            deserializePosition: deserializePosition,
+            serializeRange: serializeRange,
+            deserializeRange: deserializeRange,
+            canDeserializeRange: canDeserializeRange,
+            serializeSelection: serializeSelection,
+            deserializeSelection: deserializeSelection,
+            canDeserializeSelection: canDeserializeSelection,
+            restoreSelectionFromCookie: restoreSelectionFromCookie,
+            saveSelectionCookie: saveSelectionCookie,
+            getElementChecksum: getElementChecksum,
+            nodeToInfoString: nodeToInfoString
+        });
+
+        util.crc32 = crc32;
+    });
+    
+    return rangy;
+}, this);
\ No newline at end of file