(function() { if (!window.rangy) { window.rangy = {}; } if (window.rangy.serializeRange && window.rangy.deserializeRange) { return; } function normalizeHref(rawHref) { if (!rawHref) { return ''; } var value = String(rawHref); value = value.replace(/^.*?:\/\/[^/]+\//, ''); value = value.split('#')[0]; value = value.replace(/^\/+/, ''); try { value = decodeURIComponent(value); } catch (error) { } return value; } function nodePath(node) { var path = []; var current = node; while (current && current !== current.ownerDocument) { var parent = current.parentNode; if (!parent) { break; } var index = Array.prototype.indexOf.call(parent.childNodes, current); path.unshift(index); current = parent; } return path; } function nodeFromPath(doc, path) { var current = doc; for (var index = 0; index < path.length; index += 1) { if (!current || !current.childNodes || current.childNodes.length <= path[index]) { return null; } current = current.childNodes[path[index]]; } return current; } function documentContexts(rootDoc) { var items = []; if (!rootDoc) { return items; } items.push(rootDoc); var frames = rootDoc.querySelectorAll ? rootDoc.querySelectorAll('iframe') : []; Array.prototype.forEach.call(frames, function(frame) { try { if (!frame.contentDocument) { return; } documentContexts(frame.contentDocument).forEach(function(item) { items.push(item); }); } catch (error) { } }); return items; } window.rangy.serializeRange = function(range, doc) { var ownerDocument = doc || (range && range.startContainer && range.startContainer.ownerDocument) || document; return JSON.stringify({ kind: 'dom-range', documentHref: normalizeHref(ownerDocument.location && ownerDocument.location.href), startPath: nodePath(range.startContainer), startOffset: range.startOffset, endPath: nodePath(range.endContainer), endOffset: range.endOffset }); }; window.rangy.deserializeRange = function(rangeInfo, rootDoc) { try { var payload = typeof rangeInfo === 'string' ? JSON.parse(rangeInfo) : rangeInfo; var documentHref = normalizeHref(payload.documentHref); var resolvedDocument = (documentContexts(rootDoc || document).find(function(candidate) { return normalizeHref(candidate.location && candidate.location.href) === documentHref; })) || (rootDoc || document); var startNode = nodeFromPath(resolvedDocument, payload.startPath || []); var endNode = nodeFromPath(resolvedDocument, payload.endPath || []); if (!startNode || !endNode) { return null; } var range = resolvedDocument.createRange(); range.setStart(startNode, payload.startOffset || 0); range.setEnd(endNode, payload.endOffset || 0); return range; } catch (error) { return null; } }; })();