[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 /** 2 * @license 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 2012, Tim Down 12 * Licensed under the MIT license. 13 * Version: 1.2.3 14 * Build date: 26 February 2012 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 doc = doc || dom.getDocument(rootNode); 136 } else { 137 doc = doc || document; 138 rootNode = doc.documentElement; 139 } 140 var bits = serialized.split(":"); 141 var node = rootNode; 142 var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex; 143 144 while (i--) { 145 nodeIndex = parseInt(nodeIndices[i], 10); 146 if (nodeIndex < node.childNodes.length) { 147 node = node.childNodes[parseInt(nodeIndices[i], 10)]; 148 } else { 149 throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) + 150 " has no child with index " + nodeIndex + ", " + i); 151 } 152 } 153 154 return new dom.DomPosition(node, parseInt(bits[1], 10)); 155 } 156 157 function serializeRange(range, omitChecksum, rootNode) { 158 rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement; 159 if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) { 160 throw new Error("serializeRange: range is not wholly contained within specified root node"); 161 } 162 var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," + 163 serializePosition(range.endContainer, range.endOffset, rootNode); 164 if (!omitChecksum) { 165 serialized += "{" + getElementChecksum(rootNode) + "}"; 166 } 167 return serialized; 168 } 169 170 function deserializeRange(serialized, rootNode, doc) { 171 if (rootNode) { 172 doc = doc || dom.getDocument(rootNode); 173 } else { 174 doc = doc || document; 175 rootNode = doc.documentElement; 176 } 177 var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized); 178 var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode); 179 if (checksum && checksum !== getElementChecksum(rootNode)) { 180 throw new Error("deserializeRange: checksums of serialized range root node (" + checksum + 181 ") and target root node (" + rootNodeChecksum + ") do not match"); 182 } 183 var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc); 184 var range = api.createRange(doc); 185 range.setStart(start.node, start.offset); 186 range.setEnd(end.node, end.offset); 187 return range; 188 } 189 190 function canDeserializeRange(serialized, rootNode, doc) { 191 if (rootNode) { 192 doc = doc || dom.getDocument(rootNode); 193 } else { 194 doc = doc || document; 195 rootNode = doc.documentElement; 196 } 197 var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized); 198 var checksum = result[3]; 199 return !checksum || checksum === getElementChecksum(rootNode); 200 } 201 202 function serializeSelection(selection, omitChecksum, rootNode) { 203 selection = selection || api.getSelection(); 204 var ranges = selection.getAllRanges(), serializedRanges = []; 205 for (var i = 0, len = ranges.length; i < len; ++i) { 206 serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode); 207 } 208 return serializedRanges.join("|"); 209 } 210 211 function deserializeSelection(serialized, rootNode, win) { 212 if (rootNode) { 213 win = win || dom.getWindow(rootNode); 214 } else { 215 win = win || window; 216 rootNode = win.document.documentElement; 217 } 218 var serializedRanges = serialized.split("|"); 219 var sel = api.getSelection(win); 220 var ranges = []; 221 222 for (var i = 0, len = serializedRanges.length; i < len; ++i) { 223 ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document); 224 } 225 sel.setRanges(ranges); 226 227 return sel; 228 } 229 230 function canDeserializeSelection(serialized, rootNode, win) { 231 var doc; 232 if (rootNode) { 233 doc = win ? win.document : dom.getDocument(rootNode); 234 } else { 235 win = win || window; 236 rootNode = win.document.documentElement; 237 } 238 var serializedRanges = serialized.split("|"); 239 240 for (var i = 0, len = serializedRanges.length; i < len; ++i) { 241 if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) { 242 return false; 243 } 244 } 245 246 return true; 247 } 248 249 250 var cookieName = "rangySerializedSelection"; 251 252 function getSerializedSelectionFromCookie(cookie) { 253 var parts = cookie.split(/[;,]/); 254 for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) { 255 nameVal = parts[i].split("="); 256 if (nameVal[0].replace(/^\s+/, "") == cookieName) { 257 val = nameVal[1]; 258 if (val) { 259 return decodeURIComponent(val.replace(/\s+$/, "")); 260 } 261 } 262 } 263 return null; 264 } 265 266 function restoreSelectionFromCookie(win) { 267 win = win || window; 268 var serialized = getSerializedSelectionFromCookie(win.document.cookie); 269 if (serialized) { 270 deserializeSelection(serialized, win.doc) 271 } 272 } 273 274 function saveSelectionCookie(win, props) { 275 win = win || window; 276 props = (typeof props == "object") ? props : {}; 277 var expires = props.expires ? ";expires=" + props.expires.toUTCString() : ""; 278 var path = props.path ? ";path=" + props.path : ""; 279 var domain = props.domain ? ";domain=" + props.domain : ""; 280 var secure = props.secure ? ";secure" : ""; 281 var serialized = serializeSelection(api.getSelection(win)); 282 win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure; 283 } 284 285 api.serializePosition = serializePosition; 286 api.deserializePosition = deserializePosition; 287 288 api.serializeRange = serializeRange; 289 api.deserializeRange = deserializeRange; 290 api.canDeserializeRange = canDeserializeRange; 291 292 api.serializeSelection = serializeSelection; 293 api.deserializeSelection = deserializeSelection; 294 api.canDeserializeSelection = canDeserializeSelection; 295 296 api.restoreSelectionFromCookie = restoreSelectionFromCookie; 297 api.saveSelectionCookie = saveSelectionCookie; 298 299 api.getElementChecksum = getElementChecksum; 300 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |