├── README.md ├── .gitignore ├── package.json ├── bower.json ├── component.json ├── composer.json ├── rangy-selectionsaverestore.js ├── rangy-serializer.js ├── uncompressed ├── rangy-selectionsaverestore.js ├── rangy-serializer.js └── rangy-cssclassapplier.js ├── rangy-cssclassapplier.js └── rangy-core.js /README.md: -------------------------------------------------------------------------------- 1 | rangy 2 | ===== 3 | 4 | Shim repository for Rangy. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor 3 | build 4 | node_modules 5 | components 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "components-rangy", 3 | "version": "1.2.3", 4 | "description": "Rangy, a cross-browser JavaScript range and selection library", 5 | "keywords": ["components"], 6 | "main": "./uncompressed/rangy-core.js" 7 | } 8 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rangy", 3 | "version": "1.2.3", 4 | "description": "Rangy, a cross-browser JavaScript range and selection library", 5 | "keywords": [ 6 | "component" 7 | ], 8 | "main": [ 9 | "uncompressed/rangy-core.js", 10 | "uncompressed/rangy-cssclassapplier.js", 11 | "uncompressed/rangy-selectionsaverestore.js", 12 | "uncompressed/rangy-serializer.js" 13 | ], 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rangy", 3 | "version": "1.2.3", 4 | "description": "Rangy, a cross-browser JavaScript range and selection library", 5 | "keywords": [ 6 | "component" 7 | ], 8 | "main": "uncompressed/rangy-core.js", 9 | "scripts": [ 10 | "uncompressed/rangy-core.js", 11 | "uncompressed/rangy-cssclassapplier.js", 12 | "uncompressed/rangy-selectionsaverestore.js", 13 | "uncompressed/rangy-serializer.js" 14 | ], 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "components/rangy", 3 | "description": "Rangy, a cross-browser JavaScript range and selection library", 4 | "type": "component", 5 | "homepage": "http://code.google.com/p/rangy/", 6 | "license": "MIT", 7 | "support": { 8 | "irc": "irc://irc.freenode.org/jquery", 9 | "issues": "https://code.google.com/p/rangy/issues/list", 10 | "wiki": "https://code.google.com/p/rangy/w/list", 11 | "source": "https://code.google.com/p/rangy/source/checkout" 12 | }, 13 | "authors": [ 14 | { 15 | "name": "Tim Down" 16 | } 17 | ], 18 | "extra": { 19 | "component": { 20 | "scripts": [ 21 | "uncompressed/rangy-core.js", 22 | "uncompressed/rangy-cssclassapplier.js", 23 | "uncompressed/rangy-selectionsaverestore.js", 24 | "uncompressed/rangy-serializer.js" 25 | ], 26 | "shim": { 27 | "exports": "rangy" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rangy-selectionsaverestore.js: -------------------------------------------------------------------------------- 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 2012, Tim Down 11 | Licensed under the MIT license. 12 | Version: 1.2.3 13 | Build date: 26 February 2012 14 | */ 15 | rangy.createModule("SaveRestore",function(h,m){function n(a,g){var e="selectionBoundary_"+ +new Date+"_"+(""+Math.random()).slice(2),c,f=p.getDocument(a.startContainer),d=a.cloneRange();d.collapse(g);c=f.createElement("span");c.id=e;c.style.lineHeight="0";c.style.display="none";c.className="rangySelectionBoundary";c.appendChild(f.createTextNode(q));d.insertNode(c);d.detach();return c}function o(a,g,e,c){if(a=(a||document).getElementById(e)){g[c?"setStartBefore":"setEndBefore"](a);a.parentNode.removeChild(a)}else m.warn("Marker element has been removed. Cannot restore selection.")} 16 | function r(a,g){return g.compareBoundaryPoints(a.START_TO_START,a)}function k(a,g){var e=(a||document).getElementById(g);e&&e.parentNode.removeChild(e)}h.requireModules(["DomUtil","DomRange","WrappedRange"]);var p=h.dom,q="\ufeff";h.saveSelection=function(a){a=a||window;var g=a.document;if(h.isSelectionValid(a)){var e=h.getSelection(a),c=e.getAllRanges(),f=[],d,j;c.sort(r);for(var b=0,i=c.length;b=0;--b){d=c[b];if(d.collapsed)d.collapseBefore((g||document).getElementById(f[b].markerId));else{d.setEndBefore((g||document).getElementById(f[b].endMarkerId));d.setStartAfter((g||document).getElementById(f[b].startMarkerId))}}e.setRanges(c);return{win:a,doc:g,rangeInfos:f,restored:false}}else m.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.")}; 18 | h.restoreSelection=function(a,g){if(!a.restored){for(var e=a.rangeInfos,c=h.getSelection(a.win),f=[],d=e.length,j=d-1,b,i;j>=0;--j){b=e[j];i=h.createRange(a.doc);if(b.collapsed)if(b=(a.doc||document).getElementById(b.markerId)){b.style.display="inline";var l=b.previousSibling;if(l&&l.nodeType==3){b.parentNode.removeChild(b);i.collapseToPoint(l,l.length)}else{i.collapseBefore(b);b.parentNode.removeChild(b)}}else m.warn("Marker element has been removed. Cannot restore selection.");else{o(a.doc,i,b.startMarkerId, 19 | true);o(a.doc,i,b.endMarkerId,false)}d==1&&i.normalizeBoundaries();f[j]=i}if(d==1&&g&&h.features.selectionHasExtend&&e[0].backwards){c.removeAllRanges();c.addRange(f[0],true)}else c.setRanges(f);a.restored=true}};h.removeMarkerElement=k;h.removeMarkers=function(a){for(var g=a.rangeInfos,e=0,c=g.length,f;e/g,">");break;case 8:h=""; 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 | }); 301 | -------------------------------------------------------------------------------- /uncompressed/rangy-cssclassapplier.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license CSS Class Applier module for Rangy. 3 | * Adds, removes and toggles CSS classes on Ranges and Selections 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 2012, Tim Down 11 | * Licensed under the MIT license. 12 | * Version: 1.2.3 13 | * Build date: 26 February 2012 14 | */ 15 | rangy.createModule("CssClassApplier", function(api, module) { 16 | api.requireModules( ["WrappedSelection", "WrappedRange"] ); 17 | 18 | var dom = api.dom; 19 | 20 | 21 | 22 | var defaultTagName = "span"; 23 | 24 | function trim(str) { 25 | return str.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); 26 | } 27 | 28 | function hasClass(el, cssClass) { 29 | return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className); 30 | } 31 | 32 | function addClass(el, cssClass) { 33 | if (el.className) { 34 | if (!hasClass(el, cssClass)) { 35 | el.className += " " + cssClass; 36 | } 37 | } else { 38 | el.className = cssClass; 39 | } 40 | } 41 | 42 | var removeClass = (function() { 43 | function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) { 44 | return (whiteSpaceBefore && whiteSpaceAfter) ? " " : ""; 45 | } 46 | 47 | return function(el, cssClass) { 48 | if (el.className) { 49 | el.className = el.className.replace(new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)"), replacer); 50 | } 51 | }; 52 | })(); 53 | 54 | function sortClassName(className) { 55 | return className.split(/\s+/).sort().join(" "); 56 | } 57 | 58 | function getSortedClassName(el) { 59 | return sortClassName(el.className); 60 | } 61 | 62 | function haveSameClasses(el1, el2) { 63 | return getSortedClassName(el1) == getSortedClassName(el2); 64 | } 65 | 66 | function replaceWithOwnChildren(el) { 67 | 68 | var parent = el.parentNode; 69 | while (el.hasChildNodes()) { 70 | parent.insertBefore(el.firstChild, el); 71 | } 72 | parent.removeChild(el); 73 | } 74 | 75 | function rangeSelectsAnyText(range, textNode) { 76 | var textRange = range.cloneRange(); 77 | textRange.selectNodeContents(textNode); 78 | 79 | var intersectionRange = textRange.intersection(range); 80 | var text = intersectionRange ? intersectionRange.toString() : ""; 81 | textRange.detach(); 82 | 83 | return text != ""; 84 | } 85 | 86 | function getEffectiveTextNodes(range) { 87 | return range.getNodes([3], function(textNode) { 88 | return rangeSelectsAnyText(range, textNode); 89 | }); 90 | } 91 | 92 | function elementsHaveSameNonClassAttributes(el1, el2) { 93 | if (el1.attributes.length != el2.attributes.length) return false; 94 | for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) { 95 | attr1 = el1.attributes[i]; 96 | name = attr1.name; 97 | if (name != "class") { 98 | attr2 = el2.attributes.getNamedItem(name); 99 | if (attr1.specified != attr2.specified) return false; 100 | if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false; 101 | } 102 | } 103 | return true; 104 | } 105 | 106 | function elementHasNonClassAttributes(el, exceptions) { 107 | for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) { 108 | attrName = el.attributes[i].name; 109 | if ( !(exceptions && dom.arrayContains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") { 110 | return true; 111 | } 112 | } 113 | return false; 114 | } 115 | 116 | function elementHasProps(el, props) { 117 | for (var p in props) { 118 | if (props.hasOwnProperty(p) && el[p] !== props[p]) { 119 | return false; 120 | } 121 | } 122 | return true; 123 | } 124 | 125 | var getComputedStyleProperty; 126 | 127 | if (typeof window.getComputedStyle != "undefined") { 128 | getComputedStyleProperty = function(el, propName) { 129 | return dom.getWindow(el).getComputedStyle(el, null)[propName]; 130 | }; 131 | } else if (typeof document.documentElement.currentStyle != "undefined") { 132 | getComputedStyleProperty = function(el, propName) { 133 | return el.currentStyle[propName]; 134 | }; 135 | } else { 136 | module.fail("No means of obtaining computed style properties found"); 137 | } 138 | 139 | var isEditableElement; 140 | 141 | (function() { 142 | var testEl = document.createElement("div"); 143 | if (typeof testEl.isContentEditable == "boolean") { 144 | isEditableElement = function(node) { 145 | return node && node.nodeType == 1 && node.isContentEditable; 146 | }; 147 | } else { 148 | isEditableElement = function(node) { 149 | if (!node || node.nodeType != 1 || node.contentEditable == "false") { 150 | return false; 151 | } 152 | return node.contentEditable == "true" || isEditableElement(node.parentNode); 153 | }; 154 | } 155 | })(); 156 | 157 | function isEditingHost(node) { 158 | var parent; 159 | return node && node.nodeType == 1 160 | && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on") 161 | || (isEditableElement(node) && !isEditableElement(node.parentNode))); 162 | } 163 | 164 | function isEditable(node) { 165 | return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node); 166 | } 167 | 168 | var inlineDisplayRegex = /^inline(-block|-table)?$/i; 169 | 170 | function isNonInlineElement(node) { 171 | return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display")); 172 | } 173 | 174 | // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html) 175 | var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/; 176 | 177 | function isUnrenderedWhiteSpaceNode(node) { 178 | if (node.data.length == 0) { 179 | return true; 180 | } 181 | if (htmlNonWhiteSpaceRegex.test(node.data)) { 182 | return false; 183 | } 184 | var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace"); 185 | switch (cssWhiteSpace) { 186 | case "pre": 187 | case "pre-wrap": 188 | case "-moz-pre-wrap": 189 | return false; 190 | case "pre-line": 191 | if (/[\r\n]/.test(node.data)) { 192 | return false; 193 | } 194 | } 195 | 196 | // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a 197 | // non-inline element, it will not be rendered. This seems to be a good enough definition. 198 | return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling); 199 | } 200 | 201 | function isSplitPoint(node, offset) { 202 | if (dom.isCharacterDataNode(node)) { 203 | if (offset == 0) { 204 | return !!node.previousSibling; 205 | } else if (offset == node.length) { 206 | return !!node.nextSibling; 207 | } else { 208 | return true; 209 | } 210 | } 211 | 212 | return offset > 0 && offset < node.childNodes.length; 213 | } 214 | 215 | function splitNodeAt(node, descendantNode, descendantOffset, rangesToPreserve) { 216 | var newNode; 217 | var splitAtStart = (descendantOffset == 0); 218 | 219 | if (dom.isAncestorOf(descendantNode, node)) { 220 | 221 | return node; 222 | } 223 | 224 | if (dom.isCharacterDataNode(descendantNode)) { 225 | if (descendantOffset == 0) { 226 | descendantOffset = dom.getNodeIndex(descendantNode); 227 | descendantNode = descendantNode.parentNode; 228 | } else if (descendantOffset == descendantNode.length) { 229 | descendantOffset = dom.getNodeIndex(descendantNode) + 1; 230 | descendantNode = descendantNode.parentNode; 231 | } else { 232 | throw module.createError("splitNodeAt should not be called with offset in the middle of a data node (" 233 | + descendantOffset + " in " + descendantNode.data); 234 | } 235 | } 236 | 237 | if (isSplitPoint(descendantNode, descendantOffset)) { 238 | if (!newNode) { 239 | newNode = descendantNode.cloneNode(false); 240 | if (newNode.id) { 241 | newNode.removeAttribute("id"); 242 | } 243 | var child; 244 | while ((child = descendantNode.childNodes[descendantOffset])) { 245 | newNode.appendChild(child); 246 | } 247 | dom.insertAfter(newNode, descendantNode); 248 | } 249 | return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, dom.getNodeIndex(newNode), rangesToPreserve); 250 | } else if (node != descendantNode) { 251 | newNode = descendantNode.parentNode; 252 | 253 | // Work out a new split point in the parent node 254 | var newNodeIndex = dom.getNodeIndex(descendantNode); 255 | 256 | if (!splitAtStart) { 257 | newNodeIndex++; 258 | } 259 | return splitNodeAt(node, newNode, newNodeIndex, rangesToPreserve); 260 | } 261 | return node; 262 | } 263 | 264 | function areElementsMergeable(el1, el2) { 265 | return el1.tagName == el2.tagName && haveSameClasses(el1, el2) && elementsHaveSameNonClassAttributes(el1, el2); 266 | } 267 | 268 | function createAdjacentMergeableTextNodeGetter(forward) { 269 | var propName = forward ? "nextSibling" : "previousSibling"; 270 | 271 | return function(textNode, checkParentElement) { 272 | var el = textNode.parentNode; 273 | var adjacentNode = textNode[propName]; 274 | if (adjacentNode) { 275 | // Can merge if the node's previous/next sibling is a text node 276 | if (adjacentNode && adjacentNode.nodeType == 3) { 277 | return adjacentNode; 278 | } 279 | } else if (checkParentElement) { 280 | // Compare text node parent element with its sibling 281 | adjacentNode = el[propName]; 282 | 283 | if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) { 284 | return adjacentNode[forward ? "firstChild" : "lastChild"]; 285 | } 286 | } 287 | return null; 288 | } 289 | } 290 | 291 | var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false), 292 | getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true); 293 | 294 | 295 | function Merge(firstNode) { 296 | this.isElementMerge = (firstNode.nodeType == 1); 297 | this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode; 298 | this.textNodes = [this.firstTextNode]; 299 | } 300 | 301 | Merge.prototype = { 302 | doMerge: function() { 303 | var textBits = [], textNode, parent, text; 304 | for (var i = 0, len = this.textNodes.length; i < len; ++i) { 305 | textNode = this.textNodes[i]; 306 | parent = textNode.parentNode; 307 | textBits[i] = textNode.data; 308 | if (i) { 309 | parent.removeChild(textNode); 310 | if (!parent.hasChildNodes()) { 311 | parent.parentNode.removeChild(parent); 312 | } 313 | } 314 | } 315 | this.firstTextNode.data = text = textBits.join(""); 316 | return text; 317 | }, 318 | 319 | getLength: function() { 320 | var i = this.textNodes.length, len = 0; 321 | while (i--) { 322 | len += this.textNodes[i].length; 323 | } 324 | return len; 325 | }, 326 | 327 | toString: function() { 328 | var textBits = []; 329 | for (var i = 0, len = this.textNodes.length; i < len; ++i) { 330 | textBits[i] = "'" + this.textNodes[i].data + "'"; 331 | } 332 | return "[Merge(" + textBits.join(",") + ")]"; 333 | } 334 | }; 335 | 336 | var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly"]; 337 | 338 | // Allow "class" as a property name in object properties 339 | var mappedPropertyNames = {"class" : "className"}; 340 | 341 | function CssClassApplier(cssClass, options, tagNames) { 342 | this.cssClass = cssClass; 343 | var normalize, i, len, propName; 344 | 345 | var elementPropertiesFromOptions = null; 346 | 347 | // Initialize from options object 348 | if (typeof options == "object" && options !== null) { 349 | tagNames = options.tagNames; 350 | elementPropertiesFromOptions = options.elementProperties; 351 | 352 | for (i = 0; propName = optionProperties[i++]; ) { 353 | if (options.hasOwnProperty(propName)) { 354 | this[propName] = options[propName]; 355 | } 356 | } 357 | normalize = options.normalize; 358 | } else { 359 | normalize = options; 360 | } 361 | 362 | // Backwards compatibility: the second parameter can also be a Boolean indicating whether normalization 363 | this.normalize = (typeof normalize == "undefined") ? true : normalize; 364 | 365 | // Initialize element properties and attribute exceptions 366 | this.attrExceptions = []; 367 | var el = document.createElement(this.elementTagName); 368 | this.elementProperties = {}; 369 | for (var p in elementPropertiesFromOptions) { 370 | if (elementPropertiesFromOptions.hasOwnProperty(p)) { 371 | // Map "class" to "className" 372 | if (mappedPropertyNames.hasOwnProperty(p)) { 373 | p = mappedPropertyNames[p]; 374 | } 375 | el[p] = elementPropertiesFromOptions[p]; 376 | 377 | // Copy the property back from the dummy element so that later comparisons to check whether elements 378 | // may be removed are checking against the right value. For example, the href property of an element 379 | // returns a fully qualified URL even if it was previously assigned a relative URL. 380 | this.elementProperties[p] = el[p]; 381 | this.attrExceptions.push(p); 382 | } 383 | } 384 | 385 | this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ? 386 | sortClassName(this.elementProperties.className + " " + cssClass) : cssClass; 387 | 388 | // Initialize tag names 389 | this.applyToAnyTagName = false; 390 | var type = typeof tagNames; 391 | if (type == "string") { 392 | if (tagNames == "*") { 393 | this.applyToAnyTagName = true; 394 | } else { 395 | this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/); 396 | } 397 | } else if (type == "object" && typeof tagNames.length == "number") { 398 | this.tagNames = []; 399 | for (i = 0, len = tagNames.length; i < len; ++i) { 400 | if (tagNames[i] == "*") { 401 | this.applyToAnyTagName = true; 402 | } else { 403 | this.tagNames.push(tagNames[i].toLowerCase()); 404 | } 405 | } 406 | } else { 407 | this.tagNames = [this.elementTagName]; 408 | } 409 | } 410 | 411 | CssClassApplier.prototype = { 412 | elementTagName: defaultTagName, 413 | elementProperties: {}, 414 | ignoreWhiteSpace: true, 415 | applyToEditableOnly: false, 416 | 417 | hasClass: function(node) { 418 | return node.nodeType == 1 && dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && hasClass(node, this.cssClass); 419 | }, 420 | 421 | getSelfOrAncestorWithClass: function(node) { 422 | while (node) { 423 | if (this.hasClass(node, this.cssClass)) { 424 | return node; 425 | } 426 | node = node.parentNode; 427 | } 428 | return null; 429 | }, 430 | 431 | isModifiable: function(node) { 432 | return !this.applyToEditableOnly || isEditable(node); 433 | }, 434 | 435 | // White space adjacent to an unwrappable node can be ignored for wrapping 436 | isIgnorableWhiteSpaceNode: function(node) { 437 | return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node); 438 | }, 439 | 440 | // Normalizes nodes after applying a CSS class to a Range. 441 | postApply: function(textNodes, range, isUndo) { 442 | 443 | var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1]; 444 | 445 | var merges = [], currentMerge; 446 | 447 | var rangeStartNode = firstNode, rangeEndNode = lastNode; 448 | var rangeStartOffset = 0, rangeEndOffset = lastNode.length; 449 | 450 | var textNode, precedingTextNode; 451 | 452 | for (var i = 0, len = textNodes.length; i < len; ++i) { 453 | textNode = textNodes[i]; 454 | precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo); 455 | 456 | if (precedingTextNode) { 457 | if (!currentMerge) { 458 | currentMerge = new Merge(precedingTextNode); 459 | merges.push(currentMerge); 460 | } 461 | currentMerge.textNodes.push(textNode); 462 | if (textNode === firstNode) { 463 | rangeStartNode = currentMerge.firstTextNode; 464 | rangeStartOffset = rangeStartNode.length; 465 | } 466 | if (textNode === lastNode) { 467 | rangeEndNode = currentMerge.firstTextNode; 468 | rangeEndOffset = currentMerge.getLength(); 469 | } 470 | } else { 471 | currentMerge = null; 472 | } 473 | } 474 | 475 | // Test whether the first node after the range needs merging 476 | var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo); 477 | 478 | if (nextTextNode) { 479 | if (!currentMerge) { 480 | currentMerge = new Merge(lastNode); 481 | merges.push(currentMerge); 482 | } 483 | currentMerge.textNodes.push(nextTextNode); 484 | } 485 | 486 | // Do the merges 487 | if (merges.length) { 488 | 489 | for (i = 0, len = merges.length; i < len; ++i) { 490 | merges[i].doMerge(); 491 | } 492 | 493 | 494 | // Set the range boundaries 495 | range.setStart(rangeStartNode, rangeStartOffset); 496 | range.setEnd(rangeEndNode, rangeEndOffset); 497 | } 498 | 499 | }, 500 | 501 | createContainer: function(doc) { 502 | var el = doc.createElement(this.elementTagName); 503 | api.util.extend(el, this.elementProperties); 504 | addClass(el, this.cssClass); 505 | return el; 506 | }, 507 | 508 | applyToTextNode: function(textNode) { 509 | 510 | 511 | var parent = textNode.parentNode; 512 | if (parent.childNodes.length == 1 && dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) { 513 | addClass(parent, this.cssClass); 514 | } else { 515 | var el = this.createContainer(dom.getDocument(textNode)); 516 | textNode.parentNode.insertBefore(el, textNode); 517 | el.appendChild(textNode); 518 | } 519 | 520 | }, 521 | 522 | isRemovable: function(el) { 523 | return el.tagName.toLowerCase() == this.elementTagName 524 | && getSortedClassName(el) == this.elementSortedClassName 525 | && elementHasProps(el, this.elementProperties) 526 | && !elementHasNonClassAttributes(el, this.attrExceptions) 527 | && this.isModifiable(el); 528 | }, 529 | 530 | undoToTextNode: function(textNode, range, ancestorWithClass) { 531 | 532 | if (!range.containsNode(ancestorWithClass)) { 533 | // Split out the portion of the ancestor from which we can remove the CSS class 534 | //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass); 535 | var ancestorRange = range.cloneRange(); 536 | ancestorRange.selectNode(ancestorWithClass); 537 | 538 | if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)/* && isSplitPoint(range.endContainer, range.endOffset)*/) { 539 | splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, [range]); 540 | range.setEndAfter(ancestorWithClass); 541 | } 542 | if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)/* && isSplitPoint(range.startContainer, range.startOffset)*/) { 543 | ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, [range]); 544 | } 545 | } 546 | 547 | if (this.isRemovable(ancestorWithClass)) { 548 | replaceWithOwnChildren(ancestorWithClass); 549 | } else { 550 | removeClass(ancestorWithClass, this.cssClass); 551 | } 552 | }, 553 | 554 | applyToRange: function(range) { 555 | range.splitBoundaries(); 556 | var textNodes = getEffectiveTextNodes(range); 557 | 558 | if (textNodes.length) { 559 | var textNode; 560 | 561 | for (var i = 0, len = textNodes.length; i < len; ++i) { 562 | textNode = textNodes[i]; 563 | 564 | if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode) 565 | && this.isModifiable(textNode)) { 566 | this.applyToTextNode(textNode); 567 | } 568 | } 569 | range.setStart(textNodes[0], 0); 570 | textNode = textNodes[textNodes.length - 1]; 571 | range.setEnd(textNode, textNode.length); 572 | if (this.normalize) { 573 | this.postApply(textNodes, range, false); 574 | } 575 | } 576 | }, 577 | 578 | applyToSelection: function(win) { 579 | 580 | win = win || window; 581 | var sel = api.getSelection(win); 582 | 583 | var range, ranges = sel.getAllRanges(); 584 | sel.removeAllRanges(); 585 | var i = ranges.length; 586 | while (i--) { 587 | range = ranges[i]; 588 | this.applyToRange(range); 589 | sel.addRange(range); 590 | } 591 | 592 | }, 593 | 594 | undoToRange: function(range) { 595 | 596 | range.splitBoundaries(); 597 | var textNodes = getEffectiveTextNodes(range); 598 | var textNode, ancestorWithClass; 599 | var lastTextNode = textNodes[textNodes.length - 1]; 600 | 601 | if (textNodes.length) { 602 | for (var i = 0, len = textNodes.length; i < len; ++i) { 603 | textNode = textNodes[i]; 604 | ancestorWithClass = this.getSelfOrAncestorWithClass(textNode); 605 | if (ancestorWithClass && this.isModifiable(textNode)) { 606 | this.undoToTextNode(textNode, range, ancestorWithClass); 607 | } 608 | 609 | // Ensure the range is still valid 610 | range.setStart(textNodes[0], 0); 611 | range.setEnd(lastTextNode, lastTextNode.length); 612 | } 613 | 614 | 615 | 616 | if (this.normalize) { 617 | this.postApply(textNodes, range, true); 618 | } 619 | } 620 | }, 621 | 622 | undoToSelection: function(win) { 623 | win = win || window; 624 | var sel = api.getSelection(win); 625 | var ranges = sel.getAllRanges(), range; 626 | sel.removeAllRanges(); 627 | for (var i = 0, len = ranges.length; i < len; ++i) { 628 | range = ranges[i]; 629 | this.undoToRange(range); 630 | sel.addRange(range); 631 | } 632 | }, 633 | 634 | getTextSelectedByRange: function(textNode, range) { 635 | var textRange = range.cloneRange(); 636 | textRange.selectNodeContents(textNode); 637 | 638 | var intersectionRange = textRange.intersection(range); 639 | var text = intersectionRange ? intersectionRange.toString() : ""; 640 | textRange.detach(); 641 | 642 | return text; 643 | }, 644 | 645 | isAppliedToRange: function(range) { 646 | if (range.collapsed) { 647 | return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer); 648 | } else { 649 | var textNodes = range.getNodes( [3] ); 650 | for (var i = 0, textNode; textNode = textNodes[i++]; ) { 651 | if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode) 652 | && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) { 653 | return false; 654 | } 655 | } 656 | return true; 657 | } 658 | }, 659 | 660 | isAppliedToSelection: function(win) { 661 | win = win || window; 662 | var sel = api.getSelection(win); 663 | var ranges = sel.getAllRanges(); 664 | var i = ranges.length; 665 | while (i--) { 666 | if (!this.isAppliedToRange(ranges[i])) { 667 | return false; 668 | } 669 | } 670 | 671 | return true; 672 | }, 673 | 674 | toggleRange: function(range) { 675 | if (this.isAppliedToRange(range)) { 676 | this.undoToRange(range); 677 | } else { 678 | this.applyToRange(range); 679 | } 680 | }, 681 | 682 | toggleSelection: function(win) { 683 | if (this.isAppliedToSelection(win)) { 684 | this.undoToSelection(win); 685 | } else { 686 | this.applyToSelection(win); 687 | } 688 | }, 689 | 690 | detach: function() {} 691 | }; 692 | 693 | function createCssClassApplier(cssClass, options, tagNames) { 694 | return new CssClassApplier(cssClass, options, tagNames); 695 | } 696 | 697 | CssClassApplier.util = { 698 | hasClass: hasClass, 699 | addClass: addClass, 700 | removeClass: removeClass, 701 | hasSameClasses: haveSameClasses, 702 | replaceWithOwnChildren: replaceWithOwnChildren, 703 | elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes, 704 | elementHasNonClassAttributes: elementHasNonClassAttributes, 705 | splitNodeAt: splitNodeAt, 706 | isEditableElement: isEditableElement, 707 | isEditingHost: isEditingHost, 708 | isEditable: isEditable 709 | }; 710 | 711 | api.CssClassApplier = CssClassApplier; 712 | api.createCssClassApplier = createCssClassApplier; 713 | }); 714 | -------------------------------------------------------------------------------- /rangy-core.js: -------------------------------------------------------------------------------- 1 | /* 2 | Rangy, a cross-browser JavaScript range and selection library 3 | http://code.google.com/p/rangy/ 4 | 5 | Copyright 2012, Tim Down 6 | Licensed under the MIT license. 7 | Version: 1.2.3 8 | Build date: 26 February 2012 9 | */ 10 | window.rangy=function(){function l(p,u){var w=typeof p[u];return w=="function"||!!(w=="object"&&p[u])||w=="unknown"}function K(p,u){return!!(typeof p[u]=="object"&&p[u])}function H(p,u){return typeof p[u]!="undefined"}function I(p){return function(u,w){for(var B=w.length;B--;)if(!p(u,w[B]))return false;return true}}function z(p){return p&&A(p,x)&&v(p,t)}function C(p){window.alert("Rangy not supported in your browser. Reason: "+p);c.initialized=true;c.supported=false}function N(){if(!c.initialized){var p, 11 | u=false,w=false;if(l(document,"createRange")){p=document.createRange();if(A(p,n)&&v(p,i))u=true;p.detach()}if((p=K(document,"body")?document.body:document.getElementsByTagName("body")[0])&&l(p,"createTextRange")){p=p.createTextRange();if(z(p))w=true}!u&&!w&&C("Neither Range nor TextRange are implemented");c.initialized=true;c.features={implementsDomRange:u,implementsTextRange:w};u=k.concat(f);w=0;for(p=u.length;w["+c.childNodes.length+"]":c.nodeName}function n(c){this._next=this.root=c}function t(c,f){this.node=c;this.offset=f}function x(c){this.code=this[c]; 20 | this.codeName=c;this.message="DOMException: "+this.codeName}var A=l.util;A.areHostMethods(document,["createDocumentFragment","createElement","createTextNode"])||K.fail("document missing a Node creation method");A.isHostMethod(document,"getElementsByTagName")||K.fail("document missing getElementsByTagName method");var q=document.createElement("div");A.areHostMethods(q,["insertBefore","appendChild","cloneNode"])||K.fail("Incomplete Element implementation");A.isHostProperty(q,"innerHTML")||K.fail("Element is missing innerHTML property"); 21 | q=document.createTextNode("test");A.areHostMethods(q,["splitText","deleteData","insertData","appendData","cloneNode"])||K.fail("Incomplete Text Node implementation");var v=function(c,f){for(var k=c.length;k--;)if(c[k]===f)return true;return false};n.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){var c=this._current=this._next,f;if(this._current)if(f=c.firstChild)this._next=f;else{for(f=null;c!==this.root&&!(f=c.nextSibling);)c=c.parentNode;this._next=f}return this._current}, 22 | detach:function(){this._current=this._next=this.root=null}};t.prototype={equals:function(c){return this.node===c.node&this.offset==c.offset},inspect:function(){return"[DomPosition("+i(this.node)+":"+this.offset+")]"}};x.prototype={INDEX_SIZE_ERR:1,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INVALID_STATE_ERR:11};x.prototype.toString=function(){return this.message};l.dom={arrayContains:v,isHtmlNamespace:function(c){var f;return typeof c.namespaceURI== 23 | "undefined"||(f=c.namespaceURI)===null||f=="http://www.w3.org/1999/xhtml"},parentElement:function(c){c=c.parentNode;return c.nodeType==1?c:null},getNodeIndex:H,getNodeLength:function(c){var f;return C(c)?c.length:(f=c.childNodes)?f.length:0},getCommonAncestor:I,isAncestorOf:function(c,f,k){for(f=k?f:f.parentNode;f;)if(f===c)return true;else f=f.parentNode;return false},getClosestAncestorIn:z,isCharacterDataNode:C,insertAfter:N,splitDataNode:function(c,f){var k=c.cloneNode(false);k.deleteData(0,f); 24 | c.deleteData(f,c.length-f);N(k,c);return k},getDocument:O,getWindow:function(c){c=O(c);if(typeof c.defaultView!="undefined")return c.defaultView;else if(typeof c.parentWindow!="undefined")return c.parentWindow;else throw Error("Cannot get a window object for node");},getIframeWindow:function(c){if(typeof c.contentWindow!="undefined")return c.contentWindow;else if(typeof c.contentDocument!="undefined")return c.contentDocument.defaultView;else throw Error("getIframeWindow: No Window object found for iframe element"); 25 | },getIframeDocument:function(c){if(typeof c.contentDocument!="undefined")return c.contentDocument;else if(typeof c.contentWindow!="undefined")return c.contentWindow.document;else throw Error("getIframeWindow: No Document object found for iframe element");},getBody:function(c){return A.isHostObject(c,"body")?c.body:c.getElementsByTagName("body")[0]},getRootContainer:function(c){for(var f;f=c.parentNode;)c=f;return c},comparePoints:function(c,f,k,r){var L;if(c==k)return f===r?0:f=e.childNodes.length?e.appendChild(a):e.insertBefore(a,e.childNodes[j]);return o}function O(a){for(var e,j,o=H(a.range).createDocumentFragment();j=a.next();){e=a.isPartiallySelectedSubtree();j=j.cloneNode(!e);if(e){e=a.getSubtreeIterator();j.appendChild(O(e));e.detach(true)}if(j.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");o.appendChild(j)}return o}function i(a,e,j){var o,E;for(j=j||{stop:false};o=a.next();)if(a.isPartiallySelectedSubtree())if(e(o)=== 30 | false){j.stop=true;return}else{o=a.getSubtreeIterator();i(o,e,j);o.detach(true);if(j.stop)return}else for(o=g.createIterator(o);E=o.next();)if(e(E)===false){j.stop=true;return}}function n(a){for(var e;a.next();)if(a.isPartiallySelectedSubtree()){e=a.getSubtreeIterator();n(e);e.detach(true)}else a.remove()}function t(a){for(var e,j=H(a.range).createDocumentFragment(),o;e=a.next();){if(a.isPartiallySelectedSubtree()){e=e.cloneNode(false);o=a.getSubtreeIterator();e.appendChild(t(o));o.detach(true)}else a.remove(); 31 | if(e.nodeType==10)throw new S("HIERARCHY_REQUEST_ERR");j.appendChild(e)}return j}function x(a,e,j){var o=!!(e&&e.length),E,T=!!j;if(o)E=RegExp("^("+e.join("|")+")$");var m=[];i(new q(a,false),function(s){if((!o||E.test(s.nodeType))&&(!T||j(s)))m.push(s)});return m}function A(a){return"["+(typeof a.getName=="undefined"?"Range":a.getName())+"("+g.inspectNode(a.startContainer)+":"+a.startOffset+", "+g.inspectNode(a.endContainer)+":"+a.endOffset+")]"}function q(a,e){this.range=a;this.clonePartiallySelectedTextNodes= 32 | e;if(!a.collapsed){this.sc=a.startContainer;this.so=a.startOffset;this.ec=a.endContainer;this.eo=a.endOffset;var j=a.commonAncestorContainer;if(this.sc===this.ec&&g.isCharacterDataNode(this.sc)){this.isSingleCharacterDataNode=true;this._first=this._last=this._next=this.sc}else{this._first=this._next=this.sc===j&&!g.isCharacterDataNode(this.sc)?this.sc.childNodes[this.so]:g.getClosestAncestorIn(this.sc,j,true);this._last=this.ec===j&&!g.isCharacterDataNode(this.ec)?this.ec.childNodes[this.eo-1]:g.getClosestAncestorIn(this.ec, 33 | j,true)}}}function v(a){this.code=this[a];this.codeName=a;this.message="RangeException: "+this.codeName}function c(a,e,j){this.nodes=x(a,e,j);this._next=this.nodes[0];this._position=0}function f(a){return function(e,j){for(var o,E=j?e:e.parentNode;E;){o=E.nodeType;if(g.arrayContains(a,o))return E;E=E.parentNode}return null}}function k(a,e){if(G(a,e))throw new v("INVALID_NODE_TYPE_ERR");}function r(a){if(!a.startContainer)throw new S("INVALID_STATE_ERR");}function L(a,e){if(!g.arrayContains(e,a.nodeType))throw new v("INVALID_NODE_TYPE_ERR"); 34 | }function p(a,e){if(e<0||e>(g.isCharacterDataNode(a)?a.length:a.childNodes.length))throw new S("INDEX_SIZE_ERR");}function u(a,e){if(h(a,true)!==h(e,true))throw new S("WRONG_DOCUMENT_ERR");}function w(a){if(D(a,true))throw new S("NO_MODIFICATION_ALLOWED_ERR");}function B(a,e){if(!a)throw new S(e);}function V(a){return!!a.startContainer&&!!a.endContainer&&!(!g.arrayContains(ba,a.startContainer.nodeType)&&!h(a.startContainer,true))&&!(!g.arrayContains(ba,a.endContainer.nodeType)&&!h(a.endContainer, 35 | true))&&a.startOffset<=(g.isCharacterDataNode(a.startContainer)?a.startContainer.length:a.startContainer.childNodes.length)&&a.endOffset<=(g.isCharacterDataNode(a.endContainer)?a.endContainer.length:a.endContainer.childNodes.length)}function J(a){r(a);if(!V(a))throw Error("Range error: Range is no longer valid after DOM mutation ("+a.inspect()+")");}function ca(){}function Y(a){a.START_TO_START=ia;a.START_TO_END=la;a.END_TO_END=ra;a.END_TO_START=ma;a.NODE_BEFORE=na;a.NODE_AFTER=oa;a.NODE_BEFORE_AND_AFTER= 36 | pa;a.NODE_INSIDE=ja}function W(a){Y(a);Y(a.prototype)}function da(a,e){return function(){J(this);var j=this.startContainer,o=this.startOffset,E=this.commonAncestorContainer,T=new q(this,true);if(j!==E){j=g.getClosestAncestorIn(j,E,true);o=C(j);j=o.node;o=o.offset}i(T,w);T.reset();E=a(T);T.detach();e(this,j,o,j,o);return E}}function fa(a,e,j){function o(m,s){return function(y){r(this);L(y,$);L(d(y),ba);y=(m?z:C)(y);(s?E:T)(this,y.node,y.offset)}}function E(m,s,y){var F=m.endContainer,Q=m.endOffset; 37 | if(s!==m.startContainer||y!==m.startOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==1){F=s;Q=y}e(m,s,y,F,Q)}}function T(m,s,y){var F=m.startContainer,Q=m.startOffset;if(s!==m.endContainer||y!==m.endOffset){if(d(s)!=d(F)||g.comparePoints(s,y,F,Q)==-1){F=s;Q=y}e(m,F,Q,s,y)}}a.prototype=new ca;l.util.extend(a.prototype,{setStart:function(m,s){r(this);k(m,true);p(m,s);E(this,m,s)},setEnd:function(m,s){r(this);k(m,true);p(m,s);T(this,m,s)},setStartBefore:o(true,true),setStartAfter:o(false,true),setEndBefore:o(true, 38 | false),setEndAfter:o(false,false),collapse:function(m){J(this);m?e(this,this.startContainer,this.startOffset,this.startContainer,this.startOffset):e(this,this.endContainer,this.endOffset,this.endContainer,this.endOffset)},selectNodeContents:function(m){r(this);k(m,true);e(this,m,0,m,g.getNodeLength(m))},selectNode:function(m){r(this);k(m,false);L(m,$);var s=z(m);m=C(m);e(this,s.node,s.offset,m.node,m.offset)},extractContents:da(t,e),deleteContents:da(n,e),canSurroundContents:function(){J(this);w(this.startContainer); 39 | w(this.endContainer);var m=new q(this,true),s=m._first&&K(m._first,this)||m._last&&K(m._last,this);m.detach();return!s},detach:function(){j(this)},splitBoundaries:function(){J(this);var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=m===y;g.isCharacterDataNode(y)&&F>0&&F0&&s=g.getNodeIndex(m)&&F++;s=0}e(this,m,s,y,F)},normalizeBoundaries:function(){J(this); 40 | var m=this.startContainer,s=this.startOffset,y=this.endContainer,F=this.endOffset,Q=function(U){var R=U.nextSibling;if(R&&R.nodeType==U.nodeType){y=U;F=U.length;U.appendData(R.data);R.parentNode.removeChild(R)}},qa=function(U){var R=U.previousSibling;if(R&&R.nodeType==U.nodeType){m=U;var sa=U.length;s=R.length;U.insertData(0,R.data);R.parentNode.removeChild(R);if(m==y){F+=s;y=m}else if(y==U.parentNode){R=g.getNodeIndex(U);if(F==R){y=U;F=sa}else F>R&&F--}}},ga=true;if(g.isCharacterDataNode(y))y.length== 41 | F&&Q(y);else{if(F>0)(ga=y.childNodes[F-1])&&g.isCharacterDataNode(ga)&&Q(ga);ga=!this.collapsed}if(ga)if(g.isCharacterDataNode(m))s==0&&qa(m);else{if(sx";X=P.firstChild.nodeType==3}catch(ta){}l.features.htmlParsingConforms=X;var ka=["startContainer","startOffset","endContainer","endOffset", 47 | "collapsed","commonAncestorContainer"],ia=0,la=1,ra=2,ma=3,na=0,oa=1,pa=2,ja=3;ca.prototype={attachListener:function(a,e){this._listeners[a].push(e)},compareBoundaryPoints:function(a,e){J(this);u(this.startContainer,e.startContainer);var j=a==ma||a==ia?"start":"end",o=a==la||a==ia?"start":"end";return g.comparePoints(this[j+"Container"],this[j+"Offset"],e[o+"Container"],e[o+"Offset"])},insertNode:function(a){J(this);L(a,aa);w(this.startContainer);if(g.isAncestorOf(a,this.startContainer,true))throw new S("HIERARCHY_REQUEST_ERR"); 48 | this.setStartBefore(N(a,this.startContainer,this.startOffset))},cloneContents:function(){J(this);var a,e;if(this.collapsed)return H(this).createDocumentFragment();else{if(this.startContainer===this.endContainer&&g.isCharacterDataNode(this.startContainer)){a=this.startContainer.cloneNode(true);a.data=a.data.slice(this.startOffset,this.endOffset);e=H(this).createDocumentFragment();e.appendChild(a);return e}else{e=new q(this,true);a=O(e);e.detach()}return a}},canSurroundContents:function(){J(this);w(this.startContainer); 49 | w(this.endContainer);var a=new q(this,true),e=a._first&&K(a._first,this)||a._last&&K(a._last,this);a.detach();return!e},surroundContents:function(a){L(a,b);if(!this.canSurroundContents())throw new v("BAD_BOUNDARYPOINTS_ERR");var e=this.extractContents();if(a.hasChildNodes())for(;a.lastChild;)a.removeChild(a.lastChild);N(a,this.startContainer,this.startOffset);a.appendChild(e);this.selectNode(a)},cloneRange:function(){J(this);for(var a=new M(H(this)),e=ka.length,j;e--;){j=ka[e];a[j]=this[j]}return a}, 50 | toString:function(){J(this);var a=this.startContainer;if(a===this.endContainer&&g.isCharacterDataNode(a))return a.nodeType==3||a.nodeType==4?a.data.slice(this.startOffset,this.endOffset):"";else{var e=[];a=new q(this,true);i(a,function(j){if(j.nodeType==3||j.nodeType==4)e.push(j.data)});a.detach();return e.join("")}},compareNode:function(a){J(this);var e=a.parentNode,j=g.getNodeIndex(a);if(!e)throw new S("NOT_FOUND_ERR");a=this.comparePoint(e,j);e=this.comparePoint(e,j+1);return a<0?e>0?pa:na:e>0? 51 | oa:ja},comparePoint:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);if(g.comparePoints(a,e,this.startContainer,this.startOffset)<0)return-1;else if(g.comparePoints(a,e,this.endContainer,this.endOffset)>0)return 1;return 0},createContextualFragment:X?function(a){var e=this.startContainer,j=g.getDocument(e);if(!e)throw new S("INVALID_STATE_ERR");var o=null;if(e.nodeType==1)o=e;else if(g.isCharacterDataNode(e))o=g.parentElement(e);o=o===null||o.nodeName=="HTML"&&g.isHtmlNamespace(g.getDocument(o).documentElement)&& 52 | g.isHtmlNamespace(o)?j.createElement("body"):o.cloneNode(false);o.innerHTML=a;return g.fragmentFromNodeChildren(o)}:function(a){r(this);var e=H(this).createElement("body");e.innerHTML=a;return g.fragmentFromNodeChildren(e)},toHtml:function(){J(this);var a=H(this).createElement("div");a.appendChild(this.cloneContents());return a.innerHTML},intersectsNode:function(a,e){J(this);B(a,"NOT_FOUND_ERR");if(g.getDocument(a)!==H(this))return false;var j=a.parentNode,o=g.getNodeIndex(a);B(j,"NOT_FOUND_ERR"); 53 | var E=g.comparePoints(j,o,this.endContainer,this.endOffset);j=g.comparePoints(j,o+1,this.startContainer,this.startOffset);return e?E<=0&&j>=0:E<0&&j>0},isPointInRange:function(a,e){J(this);B(a,"HIERARCHY_REQUEST_ERR");u(a,this.startContainer);return g.comparePoints(a,e,this.startContainer,this.startOffset)>=0&&g.comparePoints(a,e,this.endContainer,this.endOffset)<=0},intersectsRange:function(a,e){J(this);if(H(a)!=H(this))throw new S("WRONG_DOCUMENT_ERR");var j=g.comparePoints(this.startContainer, 54 | this.startOffset,a.endContainer,a.endOffset),o=g.comparePoints(this.endContainer,this.endOffset,a.startContainer,a.startOffset);return e?j<=0&&o>=0:j<0&&o>0},intersection:function(a){if(this.intersectsRange(a)){var e=g.comparePoints(this.startContainer,this.startOffset,a.startContainer,a.startOffset),j=g.comparePoints(this.endContainer,this.endOffset,a.endContainer,a.endOffset),o=this.cloneRange();e==-1&&o.setStart(a.startContainer,a.startOffset);j==1&&o.setEnd(a.endContainer,a.endOffset);return o}return null}, 55 | union:function(a){if(this.intersectsRange(a,true)){var e=this.cloneRange();g.comparePoints(a.startContainer,a.startOffset,this.startContainer,this.startOffset)==-1&&e.setStart(a.startContainer,a.startOffset);g.comparePoints(a.endContainer,a.endOffset,this.endContainer,this.endOffset)==1&&e.setEnd(a.endContainer,a.endOffset);return e}else throw new v("Ranges do not intersect");},containsNode:function(a,e){return e?this.intersectsNode(a,false):this.compareNode(a)==ja},containsNodeContents:function(a){return this.comparePoint(a, 56 | 0)>=0&&this.comparePoint(a,g.getNodeLength(a))<=0},containsRange:function(a){return this.intersection(a).equals(a)},containsNodeText:function(a){var e=this.cloneRange();e.selectNode(a);var j=e.getNodes([3]);if(j.length>0){e.setStart(j[0],0);a=j.pop();e.setEnd(a,a.length);a=this.containsRange(e);e.detach();return a}else return this.containsNodeContents(a)},createNodeIterator:function(a,e){J(this);return new c(this,a,e)},getNodes:function(a,e){J(this);return x(this,a,e)},getDocument:function(){return H(this)}, 57 | collapseBefore:function(a){r(this);this.setEndBefore(a);this.collapse(false)},collapseAfter:function(a){r(this);this.setStartAfter(a);this.collapse(true)},getName:function(){return"DomRange"},equals:function(a){return M.rangesEqual(this,a)},isValid:function(){return V(this)},inspect:function(){return A(this)}};fa(M,ha,function(a){r(a);a.startContainer=a.startOffset=a.endContainer=a.endOffset=null;a.collapsed=a.commonAncestorContainer=null;I(a,"detach",null);a._listeners=null});l.rangePrototype=ca.prototype; 58 | M.rangeProperties=ka;M.RangeIterator=q;M.copyComparisonConstants=W;M.createPrototypeRange=fa;M.inspect=A;M.getRangeDocument=H;M.rangesEqual=function(a,e){return a.startContainer===e.startContainer&&a.startOffset===e.startOffset&&a.endContainer===e.endContainer&&a.endOffset===e.endOffset};l.DomRange=M;l.RangeException=v}); 59 | rangy.createModule("WrappedRange",function(l){function K(i,n,t,x){var A=i.duplicate();A.collapse(t);var q=A.parentElement();z.isAncestorOf(n,q,true)||(q=n);if(!q.canHaveHTML)return new C(q.parentNode,z.getNodeIndex(q));n=z.getDocument(q).createElement("span");var v,c=t?"StartToStart":"StartToEnd";do{q.insertBefore(n,n.previousSibling);A.moveToElementText(n)}while((v=A.compareEndPoints(c,i))>0&&n.previousSibling);c=n.nextSibling;if(v==-1&&c&&z.isCharacterDataNode(c)){A.setEndPoint(t?"EndToStart":"EndToEnd", 60 | i);if(/[\r\n]/.test(c.data)){q=A.duplicate();t=q.text.replace(/\r\n/g,"\r").length;for(t=q.moveStart("character",t);q.compareEndPoints("StartToEnd",q)==-1;){t++;q.moveStart("character",1)}}else t=A.text.length;q=new C(c,t)}else{c=(x||!t)&&n.previousSibling;q=(t=(x||t)&&n.nextSibling)&&z.isCharacterDataNode(t)?new C(t,0):c&&z.isCharacterDataNode(c)?new C(c,c.length):new C(q,z.getNodeIndex(n))}n.parentNode.removeChild(n);return q}function H(i,n){var t,x,A=i.offset,q=z.getDocument(i.node),v=q.body.createTextRange(), 61 | c=z.isCharacterDataNode(i.node);if(c){t=i.node;x=t.parentNode}else{t=i.node.childNodes;t=A12");d.close();var h=c.getIframeWindow(b).getSelection(),D=d.documentElement.lastChild.firstChild;d=d.createRange();d.setStart(D,1);d.collapse(true);h.addRange(d);ha=h.rangeCount==1;h.removeAllRanges();var G=d.cloneRange();d.setStart(D,0);G.setEnd(D,2);h.addRange(d);h.addRange(G);ea=h.rangeCount==2;d.detach();G.detach();Y.removeChild(b)}();l.features.selectionSupportsMultipleRanges=ea; 81 | l.features.collapsedNonEditableSelectionsSupported=ha;var M=false,g;if(Y&&f.isHostMethod(Y,"createControlRange")){g=Y.createControlRange();if(f.areHostProperties(g,["item","add"]))M=true}l.features.implementsControlRange=M;w=W?function(b){return b.anchorNode===b.focusNode&&b.anchorOffset===b.focusOffset}:function(b){return b.rangeCount?b.getRangeAt(b.rangeCount-1).collapsed:false};var Z;if(f.isHostMethod(B,"getRangeAt"))Z=function(b,d){try{return b.getRangeAt(d)}catch(h){return null}};else if(W)Z= 82 | function(b){var d=c.getDocument(b.anchorNode);d=l.createRange(d);d.setStart(b.anchorNode,b.anchorOffset);d.setEnd(b.focusNode,b.focusOffset);if(d.collapsed!==this.isCollapsed){d.setStart(b.focusNode,b.focusOffset);d.setEnd(b.anchorNode,b.anchorOffset)}return d};l.getSelection=function(b){b=b||window;var d=b._rangySelection,h=u(b),D=V?I(b):null;if(d){d.nativeSelection=h;d.docSelection=D;d.refresh(b)}else{d=new x(h,D,b);b._rangySelection=d}return d};l.getIframeSelection=function(b){return l.getSelection(c.getIframeWindow(b))}; 83 | g=x.prototype;if(!J&&W&&f.areHostMethods(B,["removeAllRanges","addRange"])){g.removeAllRanges=function(){this.nativeSelection.removeAllRanges();C(this)};var S=function(b,d){var h=k.getRangeDocument(d);h=l.createRange(h);h.collapseToPoint(d.endContainer,d.endOffset);b.nativeSelection.addRange(N(h));b.nativeSelection.extend(d.startContainer,d.startOffset);b.refresh()};g.addRange=fa?function(b,d){if(M&&V&&this.docSelection.type=="Control")t(this,b);else if(d&&da)S(this,b);else{var h;if(ea)h=this.rangeCount; 84 | else{this.removeAllRanges();h=0}this.nativeSelection.addRange(N(b));this.rangeCount=this.nativeSelection.rangeCount;if(this.rangeCount==h+1){if(l.config.checkSelectionRanges)if((h=Z(this.nativeSelection,this.rangeCount-1))&&!k.rangesEqual(h,b))b=new r(h);this._ranges[this.rangeCount-1]=b;z(this,b,aa(this.nativeSelection));this.isCollapsed=w(this)}else this.refresh()}}:function(b,d){if(d&&da)S(this,b);else{this.nativeSelection.addRange(N(b));this.refresh()}};g.setRanges=function(b){if(M&&b.length> 85 | 1)A(this,b);else{this.removeAllRanges();for(var d=0,h=b.length;d1)A(this,b);else d&&this.addRange(b[0])}}else{K.fail("No means of selecting a Range or TextRange was found");return false}g.getRangeAt=function(b){if(b<0||b>=this.rangeCount)throw new L("INDEX_SIZE_ERR");else return this._ranges[b]}; 87 | var $;if(J)$=function(b){var d;if(l.isSelectionValid(b.win))d=b.docSelection.createRange();else{d=c.getBody(b.win.document).createTextRange();d.collapse(true)}if(b.docSelection.type=="Control")n(b);else d&&typeof d.text!="undefined"?i(b,d):C(b)};else if(f.isHostMethod(B,"getRangeAt")&&typeof B.rangeCount=="number")$=function(b){if(M&&V&&b.docSelection.type=="Control")n(b);else{b._ranges.length=b.rangeCount=b.nativeSelection.rangeCount;if(b.rangeCount){for(var d=0,h=b.rangeCount;d