Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

updated rangy to 1.2.3; removed unused rangy files

  • Loading branch information...
commit 7a46853c18faabe59c13e831212625f3a0fab90c 1 parent 7b62621
@delambo delambo authored
View
5 assets.yml
@@ -8,10 +8,7 @@ javascripts:
sr_editor_plugin:
- lib/tinymce/jscripts/tiny_mce/plugins/icesearchreplace/editor_plugin.js
ice.min:
- - lib/rangy-1.2/rangy-core.js
- - lib/rangy-1.2/rangy-cssclassapplier.js
- - lib/rangy-1.2/rangy-selectionsaverestore.js
- - lib/rangy-1.2/rangy-serializer.js
+ - lib/rangy/rangy-core.js
- src/polyfills.js
- src/ice.js
- src/dom.js
View
9 demo/index.html
@@ -3,12 +3,9 @@
<head>
<link rel="stylesheet" href="demo.css" type="text/css" media="screen" />
- <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
- <script src="http://code.jquery.com/jquery-migrate-1.0.0.js"></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-core.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-cssclassapplier.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-selectionsaverestore.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-serializer.js'></script>
+ <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
+ <script src="http://code.jquery.com/jquery-migrate-1.0.0.js"></script>
+ <script type="text/javascript" src='../lib/rangy/rangy-core.js'></script>
<script type="text/javascript" src='../src/polyfills.js'></script>
<script type="text/javascript" src='../src/ice.js'></script>
<script type="text/javascript" src='../src/dom.js'></script>
View
712 lib/rangy-1.2/rangy-cssclassapplier.js
@@ -1,712 +0,0 @@
-/**
- * CSS Class Applier module for Rangy.
- * Adds, removes and toggles CSS classes on Ranges and Selections
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * http://code.google.com/p/rangy/
- *
- * Depends on Rangy core.
- *
- * Copyright 2011, Tim Down
- * Licensed under the MIT license.
- * Version: 1.2
- * Build date: 22 August 2011
- */
-rangy.createModule("CssClassApplier", function(api, module) {
- api.requireModules( ["WrappedSelection", "WrappedRange"] );
-
- var dom = api.dom;
-
-
-
- var defaultTagName = "span";
-
- function trim(str) {
- return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
- }
-
- function hasClass(el, cssClass) {
- return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
- }
-
- function addClass(el, cssClass) {
- if (el.className) {
- if (!hasClass(el, cssClass)) {
- el.className += " " + cssClass;
- }
- } else {
- el.className = cssClass;
- }
- }
-
- var removeClass = (function() {
- function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
- return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
- }
-
- return function(el, cssClass) {
- if (el.className) {
- el.className = el.className.replace(new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)"), replacer);
- }
- };
- })();
-
- function sortClassName(className) {
- return className.split(/\s+/).sort().join(" ");
- }
-
- function getSortedClassName(el) {
- return sortClassName(el.className);
- }
-
- function haveSameClasses(el1, el2) {
- return getSortedClassName(el1) == getSortedClassName(el2);
- }
-
- function replaceWithOwnChildren(el) {
-
- var parent = el.parentNode;
- while (el.hasChildNodes()) {
- parent.insertBefore(el.firstChild, el);
- }
- parent.removeChild(el);
- }
-
- function rangeSelectsAnyText(range, textNode) {
- var textRange = range.cloneRange();
- textRange.selectNodeContents(textNode);
-
- var intersectionRange = textRange.intersection(range);
- var text = intersectionRange ? intersectionRange.toString() : "";
- textRange.detach();
-
- return text != "";
- }
-
- function getEffectiveTextNodes(range) {
- return range.getNodes([3], function(textNode) {
- return rangeSelectsAnyText(range, textNode);
- });
- }
-
- function elementsHaveSameNonClassAttributes(el1, el2) {
- if (el1.attributes.length != el2.attributes.length) return false;
- for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
- attr1 = el1.attributes[i];
- name = attr1.name;
- if (name != "class") {
- attr2 = el2.attributes.getNamedItem(name);
- if (attr1.specified != attr2.specified) return false;
- if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
- }
- }
- return true;
- }
-
- function elementHasNonClassAttributes(el, exceptions) {
- for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
- attrName = el.attributes[i].name;
- if ( !(exceptions && dom.arrayContains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
- return true;
- }
- }
- return false;
- }
-
- function elementHasProps(el, props) {
- for (var p in props) {
- if (props.hasOwnProperty(p) && el[p] !== props[p]) {
- return false;
- }
- }
- return true;
- }
-
- var getComputedStyleProperty;
-
- if (typeof window.getComputedStyle != "undefined") {
- getComputedStyleProperty = function(el, propName) {
- return dom.getWindow(el).getComputedStyle(el, null)[propName];
- };
- } else if (typeof document.documentElement.currentStyle != "undefined") {
- getComputedStyleProperty = function(el, propName) {
- return el.currentStyle[propName];
- };
- } else {
- module.fail("No means of obtaining computed style properties found");
- }
-
- var isEditableElement;
-
- (function() {
- var testEl = document.createElement("div");
- if (typeof testEl.isContentEditable == "boolean") {
- isEditableElement = function(node) {
- return node && node.nodeType == 1 && node.isContentEditable;
- };
- } else {
- isEditableElement = function(node) {
- if (!node || node.nodeType != 1 || node.contentEditable == "false") {
- return false;
- }
- return node.contentEditable == "true" || isEditableElement(node.parentNode);
- };
- }
- })();
-
- function isEditingHost(node) {
- var parent;
- return node && node.nodeType == 1
- && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
- || (isEditableElement(node) && !isEditableElement(node.parentNode)));
- }
-
- function isEditable(node) {
- return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
- }
-
- var inlineDisplayRegex = /^inline(-block|-table)?$/i;
-
- function isNonInlineElement(node) {
- return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
- }
-
- // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
- var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
-
- function isUnrenderedWhiteSpaceNode(node) {
- if (node.data.length == 0) {
- return true;
- }
- if (htmlNonWhiteSpaceRegex.test(node.data)) {
- return false;
- }
- var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
- switch (cssWhiteSpace) {
- case "pre":
- case "pre-wrap":
- case "-moz-pre-wrap":
- return false;
- case "pre-line":
- if (/[\r\n]/.test(node.data)) {
- return false;
- }
- }
-
- // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
- // non-inline element, it will not be rendered. This seems to be a good enough definition.
- return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
- }
-
- function isSplitPoint(node, offset) {
- if (dom.isCharacterDataNode(node)) {
- if (offset == 0) {
- return !!node.previousSibling;
- } else if (offset == node.length) {
- return !!node.nextSibling;
- } else {
- return true;
- }
- }
-
- return offset > 0 && offset < node.childNodes.length;
- }
-
- function splitNodeAt(node, descendantNode, descendantOffset, rangesToPreserve) {
- var newNode;
- var splitAtStart = (descendantOffset == 0);
-
- if (dom.isAncestorOf(descendantNode, node)) {
- throw module.createError("descendant is ancestor of node");
- }
-
- if (dom.isCharacterDataNode(descendantNode)) {
- if (descendantOffset == 0) {
- descendantOffset = dom.getNodeIndex(descendantNode);
- descendantNode = descendantNode.parentNode;
- } else if (descendantOffset == descendantNode.length) {
- descendantOffset = dom.getNodeIndex(descendantNode) + 1;
- descendantNode = descendantNode.parentNode;
- } else {
- throw module.createError("splitNodeAt should not be called with offset in the middle of a data node ("
- + descendantOffset + " in " + descendantNode.data);
- }
- }
-
- if (isSplitPoint(descendantNode, descendantOffset)) {
- if (!newNode) {
- newNode = descendantNode.cloneNode(false);
- if (newNode.id) {
- newNode.removeAttribute("id");
- }
- var child;
- while ((child = descendantNode.childNodes[descendantOffset])) {
- newNode.appendChild(child);
- }
- dom.insertAfter(newNode, descendantNode);
- }
- return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, dom.getNodeIndex(newNode), rangesToPreserve);
- } else if (node != descendantNode) {
- newNode = descendantNode.parentNode;
-
- // Work out a new split point in the parent node
- var newNodeIndex = dom.getNodeIndex(descendantNode);
-
- if (!splitAtStart) {
- newNodeIndex++;
- }
- return splitNodeAt(node, newNode, newNodeIndex, rangesToPreserve);
- }
- return node;
- }
-
- function areElementsMergeable(el1, el2) {
- return el1.tagName == el2.tagName && haveSameClasses(el1, el2) && elementsHaveSameNonClassAttributes(el1, el2);
- }
-
- function createAdjacentMergeableTextNodeGetter(forward) {
- var propName = forward ? "nextSibling" : "previousSibling";
-
- return function(textNode, checkParentElement) {
- var el = textNode.parentNode;
- var adjacentNode = textNode[propName];
- if (adjacentNode) {
- // Can merge if the node's previous/next sibling is a text node
- if (adjacentNode && adjacentNode.nodeType == 3) {
- return adjacentNode;
- }
- } else if (checkParentElement) {
- // Compare text node parent element with its sibling
- adjacentNode = el[propName];
-
- if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) {
- return adjacentNode[forward ? "firstChild" : "lastChild"];
- }
- }
- return null;
- }
- }
-
- var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
- getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
-
-
- function Merge(firstNode) {
- this.isElementMerge = (firstNode.nodeType == 1);
- this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
- this.textNodes = [this.firstTextNode];
- }
-
- Merge.prototype = {
- doMerge: function() {
- var textBits = [], textNode, parent, text;
- for (var i = 0, len = this.textNodes.length; i < len; ++i) {
- textNode = this.textNodes[i];
- parent = textNode.parentNode;
- textBits[i] = textNode.data;
- if (i) {
- parent.removeChild(textNode);
- if (!parent.hasChildNodes()) {
- parent.parentNode.removeChild(parent);
- }
- }
- }
- this.firstTextNode.data = text = textBits.join("");
- return text;
- },
-
- getLength: function() {
- var i = this.textNodes.length, len = 0;
- while (i--) {
- len += this.textNodes[i].length;
- }
- return len;
- },
-
- toString: function() {
- var textBits = [];
- for (var i = 0, len = this.textNodes.length; i < len; ++i) {
- textBits[i] = "'" + this.textNodes[i].data + "'";
- }
- return "[Merge(" + textBits.join(",") + ")]";
- }
- };
-
- var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly"];
-
- // Allow "class" as a property name in object properties
- var mappedPropertyNames = {"class" : "className"};
-
- function CssClassApplier(cssClass, options, tagNames) {
- this.cssClass = cssClass;
- var normalize, i, len, propName;
-
- var elementPropertiesFromOptions = null;
-
- // Initialize from options object
- if (typeof options == "object" && options !== null) {
- tagNames = options.tagNames;
- elementPropertiesFromOptions = options.elementProperties;
-
- for (i = 0; propName = optionProperties[i++]; ) {
- if (options.hasOwnProperty(propName)) {
- this[propName] = options[propName];
- }
- }
- normalize = options.normalize;
- } else {
- normalize = options;
- }
-
- // Backwards compatibility: the second parameter can also be a Boolean indicating whether normalization
- this.normalize = (typeof normalize == "undefined") ? true : normalize;
-
- // Initialize element properties and attribute exceptions
- this.attrExceptions = [];
- var el = document.createElement(this.elementTagName);
- this.elementProperties = {};
- for (var p in elementPropertiesFromOptions) {
- if (elementPropertiesFromOptions.hasOwnProperty(p)) {
- // Map "class" to "className"
- if (mappedPropertyNames.hasOwnProperty(p)) {
- p = mappedPropertyNames[p];
- }
- el[p] = elementPropertiesFromOptions[p];
-
- // Copy the property back from the dummy element so that later comparisons to check whether elements
- // may be removed are checking against the right value. For example, the href property of an element
- // returns a fully qualified URL even if it was previously assigned a relative URL.
- this.elementProperties[p] = el[p];
- this.attrExceptions.push(p);
- }
- }
-
- this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ?
- sortClassName(this.elementProperties.className + " " + cssClass) : cssClass;
-
- // Initialize tag names
- this.applyToAnyTagName = false;
- var type = typeof tagNames;
- if (type == "string") {
- if (tagNames == "*") {
- this.applyToAnyTagName = true;
- } else {
- this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
- }
- } else if (type == "object" && typeof tagNames.length == "number") {
- this.tagNames = [];
- for (i = 0, len = tagNames.length; i < len; ++i) {
- if (tagNames[i] == "*") {
- this.applyToAnyTagName = true;
- } else {
- this.tagNames.push(tagNames[i].toLowerCase());
- }
- }
- } else {
- this.tagNames = [this.elementTagName];
- }
- }
-
- CssClassApplier.prototype = {
- elementTagName: defaultTagName,
- elementProperties: {},
- ignoreWhiteSpace: true,
- applyToEditableOnly: false,
-
- hasClass: function(node) {
- return node.nodeType == 1 && dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && hasClass(node, this.cssClass);
- },
-
- getSelfOrAncestorWithClass: function(node) {
- while (node) {
- if (this.hasClass(node, this.cssClass)) {
- return node;
- }
- node = node.parentNode;
- }
- return null;
- },
-
- isModifiable: function(node) {
- return !this.applyToEditableOnly || isEditable(node);
- },
-
- // White space adjacent to an unwrappable node can be ignored for wrapping
- isIgnorableWhiteSpaceNode: function(node) {
- return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
- },
-
- // Normalizes nodes after applying a CSS class to a Range.
- postApply: function(textNodes, range, isUndo) {
-
- var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
-
- var merges = [], currentMerge;
-
- var rangeStartNode = firstNode, rangeEndNode = lastNode;
- var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
-
- var textNode, precedingTextNode;
-
- for (var i = 0, len = textNodes.length; i < len; ++i) {
- textNode = textNodes[i];
- precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
-
- if (precedingTextNode) {
- if (!currentMerge) {
- currentMerge = new Merge(precedingTextNode);
- merges.push(currentMerge);
- }
- currentMerge.textNodes.push(textNode);
- if (textNode === firstNode) {
- rangeStartNode = currentMerge.firstTextNode;
- rangeStartOffset = rangeStartNode.length;
- }
- if (textNode === lastNode) {
- rangeEndNode = currentMerge.firstTextNode;
- rangeEndOffset = currentMerge.getLength();
- }
- } else {
- currentMerge = null;
- }
- }
-
- // Test whether the first node after the range needs merging
- var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
-
- if (nextTextNode) {
- if (!currentMerge) {
- currentMerge = new Merge(lastNode);
- merges.push(currentMerge);
- }
- currentMerge.textNodes.push(nextTextNode);
- }
-
- // Do the merges
- if (merges.length) {
-
- for (i = 0, len = merges.length; i < len; ++i) {
- merges[i].doMerge();
- }
-
-
- // Set the range boundaries
- range.setStart(rangeStartNode, rangeStartOffset);
- range.setEnd(rangeEndNode, rangeEndOffset);
- }
-
- },
-
- createContainer: function(doc) {
- var el = doc.createElement(this.elementTagName);
- api.util.extend(el, this.elementProperties);
- addClass(el, this.cssClass);
- return el;
- },
-
- applyToTextNode: function(textNode) {
-
-
- var parent = textNode.parentNode;
- if (parent.childNodes.length == 1 && dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
- addClass(parent, this.cssClass);
- } else {
- var el = this.createContainer(dom.getDocument(textNode));
- textNode.parentNode.insertBefore(el, textNode);
- el.appendChild(textNode);
- }
-
- },
-
- isRemovable: function(el) {
- return el.tagName.toLowerCase() == this.elementTagName
- && getSortedClassName(el) == this.elementSortedClassName
- && elementHasProps(el, this.elementProperties)
- && !elementHasNonClassAttributes(el, this.attrExceptions)
- && this.isModifiable(el);
- },
-
- undoToTextNode: function(textNode, range, ancestorWithClass) {
-
- if (!range.containsNode(ancestorWithClass)) {
- // Split out the portion of the ancestor from which we can remove the CSS class
- //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
- var ancestorRange = range.cloneRange();
- ancestorRange.selectNode(ancestorWithClass);
-
- if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)/* && isSplitPoint(range.endContainer, range.endOffset)*/) {
- splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, [range]);
- range.setEndAfter(ancestorWithClass);
- }
- if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)/* && isSplitPoint(range.startContainer, range.startOffset)*/) {
- ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, [range]);
- }
- }
-
- if (this.isRemovable(ancestorWithClass)) {
- replaceWithOwnChildren(ancestorWithClass);
- } else {
- removeClass(ancestorWithClass, this.cssClass);
- }
- },
-
- applyToRange: function(range) {
- range.splitBoundaries();
- var textNodes = getEffectiveTextNodes(range);
-
- if (textNodes.length) {
- var textNode;
-
- for (var i = 0, len = textNodes.length; i < len; ++i) {
- textNode = textNodes[i];
-
- if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode)
- && this.isModifiable(textNode)) {
- this.applyToTextNode(textNode);
- }
- }
- range.setStart(textNodes[0], 0);
- textNode = textNodes[textNodes.length - 1];
- range.setEnd(textNode, textNode.length);
- if (this.normalize) {
- this.postApply(textNodes, range, false);
- }
- }
- },
-
- applyToSelection: function(win) {
-
- win = win || window;
- var sel = api.getSelection(win);
-
- var range, ranges = sel.getAllRanges();
- sel.removeAllRanges();
- var i = ranges.length;
- while (i--) {
- range = ranges[i];
- this.applyToRange(range);
- sel.addRange(range);
- }
-
- },
-
- undoToRange: function(range) {
-
- range.splitBoundaries();
- var textNodes = getEffectiveTextNodes(range);
- var textNode, ancestorWithClass;
- var lastTextNode = textNodes[textNodes.length - 1];
-
- if (textNodes.length) {
- for (var i = 0, len = textNodes.length; i < len; ++i) {
- textNode = textNodes[i];
- ancestorWithClass = this.getSelfOrAncestorWithClass(textNode);
- if (ancestorWithClass && this.isModifiable(textNode)) {
- this.undoToTextNode(textNode, range, ancestorWithClass);
- }
-
- // Ensure the range is still valid
- range.setStart(textNodes[0], 0);
- range.setEnd(lastTextNode, lastTextNode.length);
- }
-
-
-
- if (this.normalize) {
- this.postApply(textNodes, range, true);
- }
- }
- },
-
- undoToSelection: function(win) {
- win = win || window;
- var sel = api.getSelection(win);
- var ranges = sel.getAllRanges(), range;
- sel.removeAllRanges();
- for (var i = 0, len = ranges.length; i < len; ++i) {
- range = ranges[i];
- this.undoToRange(range);
- sel.addRange(range);
- }
- },
-
- getTextSelectedByRange: function(textNode, range) {
- var textRange = range.cloneRange();
- textRange.selectNodeContents(textNode);
-
- var intersectionRange = textRange.intersection(range);
- var text = intersectionRange ? intersectionRange.toString() : "";
- textRange.detach();
-
- return text;
- },
-
- isAppliedToRange: function(range) {
- if (range.collapsed) {
- return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
- } else {
- var textNodes = range.getNodes( [3] );
- for (var i = 0, textNode; textNode = textNodes[i++]; ) {
- if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode)
- && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
- return false;
- }
- }
- return true;
- }
- },
-
- isAppliedToSelection: function(win) {
- win = win || window;
- var sel = api.getSelection(win);
- var ranges = sel.getAllRanges();
- var i = ranges.length;
- while (i--) {
- if (!this.isAppliedToRange(ranges[i])) {
- return false;
- }
- }
-
- return true;
- },
-
- toggleRange: function(range) {
- if (this.isAppliedToRange(range)) {
- this.undoToRange(range);
- } else {
- this.applyToRange(range);
- }
- },
-
- toggleSelection: function(win) {
- if (this.isAppliedToSelection(win)) {
- this.undoToSelection(win);
- } else {
- this.applyToSelection(win);
- }
- },
-
- detach: function() {}
- };
-
- function createCssClassApplier(cssClass, options, tagNames) {
- return new CssClassApplier(cssClass, options, tagNames);
- }
-
- CssClassApplier.util = {
- hasClass: hasClass,
- addClass: addClass,
- removeClass: removeClass,
- hasSameClasses: haveSameClasses,
- replaceWithOwnChildren: replaceWithOwnChildren,
- elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
- elementHasNonClassAttributes: elementHasNonClassAttributes,
- splitNodeAt: splitNodeAt,
- isEditableElement: isEditableElement,
- isEditingHost: isEditingHost,
- isEditable: isEditable
- };
-
- api.CssClassApplier = CssClassApplier;
- api.createCssClassApplier = createCssClassApplier;
-});
View
195 lib/rangy-1.2/rangy-selectionsaverestore.js
@@ -1,195 +0,0 @@
-/**
- * Selection save and restore module for Rangy.
- * Saves and restores user selections using marker invisible elements in the DOM.
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * http://code.google.com/p/rangy/
- *
- * Depends on Rangy core.
- *
- * Copyright 2011, Tim Down
- * Licensed under the MIT license.
- * Version: 1.2
- * Build date: 22 August 2011
- */
-rangy.createModule("SaveRestore", function(api, module) {
- api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
-
- var dom = api.dom;
-
- var markerTextChar = "\ufeff";
-
- function gEBI(id, doc) {
- return (doc || document).getElementById(id);
- }
-
- function insertRangeBoundaryMarker(range, atStart) {
- var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
- var markerEl;
- var doc = dom.getDocument(range.startContainer);
-
- // Clone the Range and collapse to the appropriate boundary point
- var boundaryRange = range.cloneRange();
- boundaryRange.collapse(atStart);
-
- // Create the marker element containing a single invisible character using DOM methods and insert it
- markerEl = doc.createElement("span");
- markerEl.id = markerId;
- markerEl.style.lineHeight = "0";
- markerEl.style.display = "none";
- markerEl.className = "rangySelectionBoundary";
- markerEl.appendChild(doc.createTextNode(markerTextChar));
-
- boundaryRange.insertNode(markerEl);
- boundaryRange.detach();
- return markerEl;
- }
-
- function setRangeBoundary(doc, range, markerId, atStart) {
- var markerEl = gEBI(markerId, doc);
- if (markerEl) {
- range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
- markerEl.parentNode.removeChild(markerEl);
- } else {
- module.warn("Marker element has been removed. Cannot restore selection.");
- }
- }
-
- function compareRanges(r1, r2) {
- return r2.compareBoundaryPoints(r1.START_TO_START, r1);
- }
-
- function saveSelection(win) {
- win = win || window;
- var doc = win.document;
- if (!api.isSelectionValid(win)) {
- module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
- return;
- }
- var sel = api.getSelection(win);
- var ranges = sel.getAllRanges();
- var rangeInfos = [], startEl, endEl, range;
-
- // Order the ranges by position within the DOM, latest first
- ranges.sort(compareRanges);
-
- for (var i = 0, len = ranges.length; i < len; ++i) {
- range = ranges[i];
- if (range.collapsed) {
- endEl = insertRangeBoundaryMarker(range, false);
- rangeInfos.push({
- markerId: endEl.id,
- collapsed: true
- });
- } else {
- endEl = insertRangeBoundaryMarker(range, false);
- startEl = insertRangeBoundaryMarker(range, true);
-
- rangeInfos[i] = {
- startMarkerId: startEl.id,
- endMarkerId: endEl.id,
- collapsed: false,
- backwards: ranges.length == 1 && sel.isBackwards()
- };
- }
- }
-
- // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
- // between its markers
- for (i = len - 1; i >= 0; --i) {
- range = ranges[i];
- if (range.collapsed) {
- range.collapseBefore(gEBI(rangeInfos[i].markerId, doc));
- } else {
- range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
- range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
- }
- }
-
- // Ensure current selection is unaffected
- sel.setRanges(ranges);
- return {
- win: win,
- doc: doc,
- rangeInfos: rangeInfos,
- restored: false
- };
- }
-
- function restoreSelection(savedSelection, preserveDirection) {
- if (!savedSelection.restored) {
- var rangeInfos = savedSelection.rangeInfos;
- var sel = api.getSelection(savedSelection.win);
- var ranges = [];
-
- // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
- // normalization affecting previously restored ranges.
- for (var len = rangeInfos.length, i = len - 1, rangeInfo, range; i >= 0; --i) {
- rangeInfo = rangeInfos[i];
- range = api.createRange(savedSelection.doc);
- if (rangeInfo.collapsed) {
- var markerEl = gEBI(rangeInfo.markerId, savedSelection.doc);
- if (markerEl) {
- markerEl.style.display = "inline";
- var previousNode = markerEl.previousSibling;
-
- // Workaround for issue 17
- if (previousNode && previousNode.nodeType == 3) {
- markerEl.parentNode.removeChild(markerEl);
- range.collapseToPoint(previousNode, previousNode.length);
- } else {
- range.collapseBefore(markerEl);
- markerEl.parentNode.removeChild(markerEl);
- }
- } else {
- module.warn("Marker element has been removed. Cannot restore selection.");
- }
- } else {
- setRangeBoundary(savedSelection.doc, range, rangeInfo.startMarkerId, true);
- setRangeBoundary(savedSelection.doc, range, rangeInfo.endMarkerId, false);
- }
-
- // Normalizing range boundaries is only viable if the selection contains only one range. For example,
- // if the selection contained two ranges that were both contained within the same single text node,
- // both would alter the same text node when restoring and break the other range.
- if (len == 1) {
- range.normalizeBoundaries();
- }
- ranges[i] = range;
- }
- if (len == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backwards) {
- sel.removeAllRanges();
- sel.addRange(ranges[0], true);
- } else {
- sel.setRanges(ranges);
- }
-
- savedSelection.restored = true;
- }
- }
-
- function removeMarkerElement(doc, markerId) {
- var markerEl = gEBI(markerId, doc);
- if (markerEl) {
- markerEl.parentNode.removeChild(markerEl);
- }
- }
-
- function removeMarkers(savedSelection) {
- var rangeInfos = savedSelection.rangeInfos;
- for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
- rangeInfo = rangeInfos[i];
- if (rangeInfo.collapsed) {
- removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
- } else {
- removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
- removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
- }
- }
- }
-
- api.saveSelection = saveSelection;
- api.restoreSelection = restoreSelection;
- api.removeMarkerElement = removeMarkerElement;
- api.removeMarkers = removeMarkers;
-});
View
300 lib/rangy-1.2/rangy-serializer.js
@@ -1,300 +0,0 @@
-/**
- * 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
- * http://code.google.com/p/rangy/
- *
- * Depends on Rangy core.
- *
- * Copyright 2011, Tim Down
- * Licensed under the MIT license.
- * Version: 1.2
- * Build date: 22 August 2011
- */
-rangy.createModule("Serializer", function(api, module) {
- api.requireModules( ["WrappedSelection", "WrappedRange"] );
- var UNDEF = "undefined";
-
- // encodeURIComponent and decodeURIComponent are required for cookie handling
- if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
- module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");
- }
-
- // 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 pathBits = [], n = node;
- rootNode = rootNode || dom.getDocument(node).documentElement;
- while (n && n != rootNode) {
- pathBits.push(dom.getNodeIndex(n, true));
- n = n.parentNode;
- }
- return pathBits.join("/") + ":" + offset;
- }
-
- function deserializePosition(serialized, rootNode, doc) {
- if (rootNode) {
- doc = doc || dom.getDocument(rootNode);
- } else {
- doc = doc || document;
- rootNode = doc.documentElement;
- }
- var bits = serialized.split(":");
- var node = rootNode;
- var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex;
-
- while (i--) {
- nodeIndex = parseInt(nodeIndices[i], 10);
- if (nodeIndex < node.childNodes.length) {
- node = node.childNodes[parseInt(nodeIndices[i], 10)];
- } else {
- throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) +
- " has no child with index " + nodeIndex + ", " + i);
- }
- }
-
- return new dom.DomPosition(node, parseInt(bits[1], 10));
- }
-
- function serializeRange(range, omitChecksum, rootNode) {
- rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
- if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) {
- throw new Error("serializeRange: range is not wholly contained within specified root node");
- }
- var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
- serializePosition(range.endContainer, range.endOffset, rootNode);
- if (!omitChecksum) {
- serialized += "{" + getElementChecksum(rootNode) + "}";
- }
- return serialized;
- }
-
- function deserializeRange(serialized, rootNode, doc) {
- if (rootNode) {
- doc = doc || dom.getDocument(rootNode);
- } else {
- doc = doc || document;
- rootNode = doc.documentElement;
- }
- var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized);
- var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);
- if (checksum && checksum !== getElementChecksum(rootNode)) {
- throw new Error("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.setStart(start.node, start.offset);
- range.setEnd(end.node, end.offset);
- return range;
- }
-
- function canDeserializeRange(serialized, rootNode, doc) {
- if (rootNode) {
- doc = doc || dom.getDocument(rootNode);
- } else {
- doc = doc || document;
- rootNode = doc.documentElement;
- }
- var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized);
- var checksum = result[3];
- return !checksum || checksum === getElementChecksum(rootNode);
- }
-
- function serializeSelection(selection, omitChecksum, rootNode) {
- selection = selection || api.getSelection();
- 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;
- }
-
- api.serializePosition = serializePosition;
- api.deserializePosition = deserializePosition;
-
- api.serializeRange = serializeRange;
- api.deserializeRange = deserializeRange;
- api.canDeserializeRange = canDeserializeRange;
-
- api.serializeSelection = serializeSelection;
- api.deserializeSelection = deserializeSelection;
- api.canDeserializeSelection = canDeserializeSelection;
-
- api.restoreSelectionFromCookie = restoreSelectionFromCookie;
- api.saveSelectionCookie = saveSelectionCookie;
-
- api.getElementChecksum = getElementChecksum;
-});
View
179 lib/rangy-1.2/rangy-core.js → lib/rangy/rangy-core.js
@@ -1,11 +1,11 @@
/**
- * Rangy, a cross-browser JavaScript range and selection library
+ * @license Rangy, a cross-browser JavaScript range and selection library
* http://code.google.com/p/rangy/
*
- * Copyright 2011, Tim Down
+ * Copyright 2012, Tim Down
* Licensed under the MIT license.
- * Version: 1.2
- * Build date: 22 August 2011
+ * Version: 1.2.3
+ * Build date: 26 February 2012
*/
window['rangy'] = (function() {
@@ -65,7 +65,7 @@ window['rangy'] = (function() {
}
var api = {
- version: "1.2",
+ version: "1.2.3",
initialized: false,
supported: true,
@@ -312,6 +312,11 @@ rangy.createModule("DomUtil", function(api, module) {
module.fail("Incomplete Element implementation");
}
+ // innerHTML is required for Range's createContextualFragment method
+ if (!util.isHostProperty(el, "innerHTML")) {
+ module.fail("Element is missing innerHTML property");
+ }
+
var textNode = document.createTextNode("test");
if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
!util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
@@ -339,6 +344,17 @@ rangy.createModule("DomUtil", function(api, module) {
return false;
};
+ // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
+ function isHtmlNamespace(node) {
+ var ns;
+ return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
+ }
+
+ function parentElement(node) {
+ var parent = node.parentNode;
+ return (parent.nodeType == 1) ? parent : null;
+ }
+
function getNodeIndex(node) {
var i = 0;
while( (node = node.previousSibling) ) {
@@ -513,6 +529,14 @@ rangy.createModule("DomUtil", function(api, module) {
}
}
+ function fragmentFromNodeChildren(node) {
+ var fragment = getDocument(node).createDocumentFragment(), child;
+ while ( (child = node.firstChild) ) {
+ fragment.appendChild(child);
+ }
+ return fragment;
+ }
+
function inspectNode(node) {
if (!node) {
return "[No node]";
@@ -612,6 +636,8 @@ rangy.createModule("DomUtil", function(api, module) {
api.dom = {
arrayContains: arrayContains,
+ isHtmlNamespace: isHtmlNamespace,
+ parentElement: parentElement,
getNodeIndex: getNodeIndex,
getNodeLength: getNodeLength,
getCommonAncestor: getCommonAncestor,
@@ -628,6 +654,7 @@ rangy.createModule("DomUtil", function(api, module) {
getRootContainer: getRootContainer,
comparePoints: comparePoints,
inspectNode: inspectNode,
+ fragmentFromNodeChildren: fragmentFromNodeChildren,
createIterator: createIterator,
DomPosition: DomPosition
};
@@ -1052,17 +1079,109 @@ rangy.createModule("DomUtil", function(api, module) {
return offset <= (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length);
}
+ function isRangeValid(range) {
+ return (!!range.startContainer && !!range.endContainer
+ && !isOrphan(range.startContainer)
+ && !isOrphan(range.endContainer)
+ && isValidOffset(range.startContainer, range.startOffset)
+ && isValidOffset(range.endContainer, range.endOffset));
+ }
+
function assertRangeValid(range) {
assertNotDetached(range);
- if (isOrphan(range.startContainer) || isOrphan(range.endContainer) ||
- !isValidOffset(range.startContainer, range.startOffset) ||
- !isValidOffset(range.endContainer, range.endOffset)) {
+ if (!isRangeValid(range)) {
throw new Error("Range error: Range is no longer valid after DOM mutation (" + range.inspect() + ")");
}
}
/*----------------------------------------------------------------------------------------------------------------*/
+ // Test the browser's innerHTML support to decide how to implement createContextualFragment
+ var styleEl = document.createElement("style");
+ var htmlParsingConforms = false;
+ try {
+ styleEl.innerHTML = "<b>x</b>";
+ htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
+ } catch (e) {
+ // IE 6 and 7 throw
+ }
+
+ api.features.htmlParsingConforms = htmlParsingConforms;
+
+ var createContextualFragment = htmlParsingConforms ?
+
+ // Implementation as per HTML parsing spec, trusting in the browser's implementation of innerHTML. See
+ // discussion and base code for this implementation at issue 67.
+ // Spec: http://html5.org/specs/dom-parsing.html#extensions-to-the-range-interface
+ // Thanks to Aleks Williams.
+ function(fragmentStr) {
+ // "Let node the context object's start's node."
+ var node = this.startContainer;
+ var doc = dom.getDocument(node);
+
+ // "If the context object's start's node is null, raise an INVALID_STATE_ERR
+ // exception and abort these steps."
+ if (!node) {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+
+ // "Let element be as follows, depending on node's interface:"
+ // Document, Document Fragment: null
+ var el = null;
+
+ // "Element: node"
+ if (node.nodeType == 1) {
+ el = node;
+
+ // "Text, Comment: node's parentElement"
+ } else if (dom.isCharacterDataNode(node)) {
+ el = dom.parentElement(node);
+ }
+
+ // "If either element is null or element's ownerDocument is an HTML document
+ // and element's local name is "html" and element's namespace is the HTML
+ // namespace"
+ if (el === null || (
+ el.nodeName == "HTML"
+ && dom.isHtmlNamespace(dom.getDocument(el).documentElement)
+ && dom.isHtmlNamespace(el)
+ )) {
+
+ // "let element be a new Element with "body" as its local name and the HTML
+ // namespace as its namespace.""
+ el = doc.createElement("body");
+ } else {
+ el = el.cloneNode(false);
+ }
+
+ // "If the node's document is an HTML document: Invoke the HTML fragment parsing algorithm."
+ // "If the node's document is an XML document: Invoke the XML fragment parsing algorithm."
+ // "In either case, the algorithm must be invoked with fragment as the input
+ // and element as the context element."
+ el.innerHTML = fragmentStr;
+
+ // "If this raises an exception, then abort these steps. Otherwise, let new
+ // children be the nodes returned."
+
+ // "Let fragment be a new DocumentFragment."
+ // "Append all new children to fragment."
+ // "Return fragment."
+ return dom.fragmentFromNodeChildren(el);
+ } :
+
+ // In this case, innerHTML cannot be trusted, so fall back to a simpler, non-conformant implementation that
+ // previous versions of Rangy used (with the exception of using a body element rather than a div)
+ function(fragmentStr) {
+ assertNotDetached(this);
+ var doc = getRangeDocument(this);
+ var el = doc.createElement("body");
+ el.innerHTML = fragmentStr;
+
+ return dom.fragmentFromNodeChildren(el);
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
var rangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
"commonAncestorContainer"];
@@ -1234,22 +1353,7 @@ rangy.createModule("DomUtil", function(api, module) {
return 0;
},
- createContextualFragment: function(html) {
- assertNotDetached(this);
- var doc = getRangeDocument(this);
- var container = doc.createElement("div");
-
- // The next line is obviously non-standard but will work in all recent browsers
- container.innerHTML = html;
-
- var frag = doc.createDocumentFragment(), n;
-
- while ( (n = container.firstChild) ) {
- frag.appendChild(n);
- }
-
- return frag;
- },
+ createContextualFragment: createContextualFragment,
toHtml: function() {
assertRangeValid(this);
@@ -1403,6 +1507,10 @@ rangy.createModule("DomUtil", function(api, module) {
return Range.rangesEqual(this, range);
},
+ isValid: function() {
+ return isRangeValid(this);
+ },
+
inspect: function() {
return inspect(this);
}
@@ -1472,7 +1580,7 @@ rangy.createModule("DomUtil", function(api, module) {
function setRangeStart(range, node, offset) {
var ec = range.endContainer, eo = range.endOffset;
- if (node !== range.startContainer || offset !== this.startOffset) {
+ if (node !== range.startContainer || offset !== range.startOffset) {
// Check the root containers of the range and the new boundary, and also check whether the new boundary
// is after the current end. In either case, collapse the range to the new position
if (getRootContainer(node) != getRootContainer(ec) || dom.comparePoints(node, offset, ec, eo) == 1) {
@@ -1485,7 +1593,7 @@ rangy.createModule("DomUtil", function(api, module) {
function setRangeEnd(range, node, offset) {
var sc = range.startContainer, so = range.startOffset;
- if (node !== range.endContainer || offset !== this.endOffset) {
+ if (node !== range.endContainer || offset !== range.endOffset) {
// Check the root containers of the range and the new boundary, and also check whether the new boundary
// is after the current end. In either case, collapse the range to the new position
if (getRootContainer(node) != getRootContainer(sc) || dom.comparePoints(node, offset, sc, so) == -1) {
@@ -1497,7 +1605,7 @@ rangy.createModule("DomUtil", function(api, module) {
}
function setRangeStartAndEnd(range, node, offset) {
- if (node !== range.startContainer || offset !== this.startOffset || node !== range.endContainer || offset !== this.endOffset) {
+ if (node !== range.startContainer || offset !== range.startOffset || node !== range.endContainer || offset !== range.endOffset) {
boundaryUpdater(range, node, offset, node, offset);
}
}
@@ -1972,7 +2080,7 @@ rangy.createModule("DomUtil", function(api, module) {
if (api.features.implementsDomRange && (!api.features.implementsTextRange || !api.config.preferTextRange)) {
// This is a wrapper around the browser's native DOM Range. It has two aims:
// - Provide workarounds for specific browser bugs
- // - provide convenient extensions, as found in Rangy's DomRange
+ // - provide convenient extensions, which are inherited from Rangy's DomRange
(function() {
var rangeProto;
@@ -2206,6 +2314,15 @@ rangy.createModule("DomUtil", function(api, module) {
/*--------------------------------------------------------------------------------------------------------*/
+ // Test for existence of createContextualFragment and delegate to it if it exists
+ if (api.util.isHostMethod(range, "createContextualFragment")) {
+ rangeProto.createContextualFragment = function(fragmentStr) {
+ return this.nativeRange.createContextualFragment(fragmentStr);
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
// Clean up
dom.getBody(document).removeChild(testTextNode);
range.detach();
@@ -2394,6 +2511,9 @@ rangy.createModule("DomUtil", function(api, module) {
(function() {
var iframe = document.createElement("iframe");
+ iframe.frameBorder = 0;
+ iframe.style.position = "absolute";
+ iframe.style.left = "-10000px";
body.appendChild(iframe);
var iframeDoc = dom.getIframeDocument(iframe);
@@ -2692,8 +2812,7 @@ rangy.createModule("DomUtil", function(api, module) {
this.removeAllRanges();
previousRangeCount = 0;
}
- var nr = getNativeRange(range);
- this.nativeSelection.addRange(nr);
+ this.nativeSelection.addRange(getNativeRange(range));
// Check whether adding the range was successful
this.rangeCount = this.nativeSelection.rangeCount;
View
5 test/test.html
@@ -8,10 +8,7 @@
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="vendor/qunit.js"></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-core.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-cssclassapplier.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-selectionsaverestore.js'></script>
- <script type="text/javascript" src='../lib/rangy-1.2/rangy-serializer.js'></script>
+ <script type="text/javascript" src='../lib/rangy/rangy-core.js'></script>
<script type="text/javascript" src='../src/ice.js'></script>
<script type="text/javascript" src='../src/dom.js'></script>
<script type="text/javascript" src='../src/icePlugin.js'></script>
Please sign in to comment.
Something went wrong with that request. Please try again.