Skip to content

Commit

Permalink
Fixes to bring Range implementation into line with DOM4 spec
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim committed Jul 13, 2014
1 parent 74fa31e commit d6026de
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 2,552 deletions.
10 changes: 3 additions & 7 deletions demos/core.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,10 @@
if (range) {
var el = document.createElement("span");
el.style.backgroundColor = "pink";
try {
if (range.canSurroundContents(el)) {
range.surroundContents(el);
} catch(ex) {
if ((ex instanceof rangy.RangeException || Object.prototype.toString.call(ex) == "[object RangeException]") && ex.code == 1) {
alert("Unable to surround range because range partially selects a non-text node. See DOM Level 2 Range spec for more information.\n\n" + ex);
} else {
alert("Unexpected errror: " + ex);
}
} else {
alert("Unable to surround range because range partially selects a non-text node. See DOM4 spec for more information.");
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/js/core/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,8 @@ rangy.createCoreModule("DomUtil", [], function(api, module) {
NO_MODIFICATION_ALLOWED_ERR: 7,
NOT_FOUND_ERR: 8,
NOT_SUPPORTED_ERR: 9,
INVALID_STATE_ERR: 11
INVALID_STATE_ERR: 11,
INVALID_NODE_TYPE_ERR: 24
};

DOMException.prototype.toString = function() {
Expand Down
69 changes: 11 additions & 58 deletions src/js/core/domrange.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,25 +316,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {

/*----------------------------------------------------------------------------------------------------------------*/

// Exceptions

function RangeException(codeName) {
this.code = this[codeName];
this.codeName = codeName;
this.message = "RangeException: " + this.codeName;
}

RangeException.prototype = {
BAD_BOUNDARYPOINTS_ERR: 1,
INVALID_NODE_TYPE_ERR: 2
};

RangeException.prototype.toString = function() {
return this.message;
};

/*----------------------------------------------------------------------------------------------------------------*/

var beforeAfterNodeTypes = [1, 3, 4, 5, 7, 8, 10];
var rootContainerNodeTypes = [2, 9, 11];
var readonlyNodeTypes = [5, 6, 10, 12];
Expand All @@ -361,19 +342,13 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {

function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
throw new RangeException("INVALID_NODE_TYPE_ERR");
}
}

function assertNotDetached(range) {
if (!range.startContainer) {
throw new DOMException("INVALID_STATE_ERR");
throw new DOMException("INVALID_NODE_TYPE_ERR");
}
}

function assertValidNodeType(node, invalidTypes) {
if (!arrayContains(invalidTypes, node.nodeType)) {
throw new RangeException("INVALID_NODE_TYPE_ERR");
throw new DOMException("INVALID_NODE_TYPE_ERR");
}
}

Expand Down Expand Up @@ -419,7 +394,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
}

function assertRangeValid(range) {
assertNotDetached(range);
if (!isRangeValid(range)) {
throw new Error("Range error: Range is no longer valid after DOM mutation (" + range.inspect() + ")");
}
Expand Down Expand Up @@ -503,7 +477,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
// 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;
Expand Down Expand Up @@ -619,7 +592,7 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
assertValidNodeType(node, surroundNodeTypes);

if (!this.canSurroundContents()) {
throw new RangeException("BAD_BOUNDARYPOINTS_ERR");
throw new DOMException("INVALID_STATE_ERR");
}

// Extract the contents
Expand Down Expand Up @@ -782,7 +755,7 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
}
return unionRange;
} else {
throw new RangeException("Ranges do not intersect");
throw new DOMException("Ranges do not intersect");
}
},

Expand Down Expand Up @@ -811,8 +784,7 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
nodeRange.setStart(textNodes[0], 0);
var lastTextNode = textNodes.pop();
nodeRange.setEnd(lastTextNode, lastTextNode.length);
var contains = this.containsRange(nodeRange);
return contains;
return this.containsRange(nodeRange);
} else {
return this.containsNodeContents(node);
}
Expand All @@ -828,15 +800,11 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
},

collapseBefore: function(node) {
assertNotDetached(this);

this.setEndBefore(node);
this.collapse(false);
},

collapseAfter: function(node) {
assertNotDetached(this);

this.setStartAfter(node);
this.collapse(true);
},
Expand Down Expand Up @@ -905,6 +873,10 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {

inspect: function() {
return inspect(this);
},

detach: function() {
// In DOM4, detach() is now a no-op.
}
});

Expand Down Expand Up @@ -958,10 +930,9 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
};
}

function createPrototypeRange(constructor, boundaryUpdater, detacher) {
function createPrototypeRange(constructor, boundaryUpdater) {
function createBeforeAfterNodeSetter(isBefore, isStart) {
return function(node) {
assertNotDetached(this);
assertValidNodeType(node, beforeAfterNodeTypes);
assertValidNodeType(getRootContainer(node), rootContainerNodeTypes);

Expand Down Expand Up @@ -1003,15 +974,13 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {

util.extend(constructor.prototype, {
setStart: function(node, offset) {
assertNotDetached(this);
assertNoDocTypeNotationEntityAncestor(node, true);
assertValidOffset(node, offset);

setRangeStart(this, node, offset);
},

setEnd: function(node, offset) {
assertNotDetached(this);
assertNoDocTypeNotationEntityAncestor(node, true);
assertValidOffset(node, offset);

Expand All @@ -1027,8 +996,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
* startNode and ending at endOffset in endNode
*/
setStartAndEnd: function() {
assertNotDetached(this);

var args = arguments;
var sc = args[0], so = args[1], ec = sc, eo = so;

Expand Down Expand Up @@ -1064,14 +1031,12 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
},

selectNodeContents: function(node) {
assertNotDetached(this);
assertNoDocTypeNotationEntityAncestor(node, true);

boundaryUpdater(this, node, 0, node, getNodeLength(node));
},

selectNode: function(node) {
assertNotDetached(this);
assertNoDocTypeNotationEntityAncestor(node, false);
assertValidNodeType(node, beforeAfterNodeTypes);

Expand All @@ -1097,10 +1062,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
return !boundariesInvalid;
},

detach: function() {
detacher(this);
},

splitBoundaries: function() {
splitRangeBoundaries(this);
},
Expand Down Expand Up @@ -1185,7 +1146,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
},

collapseToPoint: function(node, offset) {
assertNotDetached(this);
assertNoDocTypeNotationEntityAncestor(node, true);
assertValidOffset(node, offset);
this.setStartAndEnd(node, offset);
Expand Down Expand Up @@ -1214,12 +1174,6 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
updateCollapsedAndCommonAncestor(range);
}

function detach(range) {
assertNotDetached(range);
range.startContainer = range.startOffset = range.endContainer = range.endOffset = range.document = null;
range.collapsed = range.commonAncestorContainer = null;
}

function Range(doc) {
this.startContainer = doc;
this.startOffset = 0;
Expand All @@ -1229,7 +1183,7 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
updateCollapsedAndCommonAncestor(this);
}

createPrototypeRange(Range, updateBoundaries, detach);
createPrototypeRange(Range, updateBoundaries);

util.extend(Range, {
rangeProperties: rangeProperties,
Expand All @@ -1247,5 +1201,4 @@ rangy.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
});

api.DomRange = Range;
api.RangeException = RangeException;
});
10 changes: 1 addition & 9 deletions src/js/core/wrappedrange.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ rangy.createCoreModule("WrappedRange", ["DomRange"], function(api, module) {
}
}

function detach(range) {
range.detached = true;
var i = rangeProperties.length;
while (i--) {
range[ rangeProperties[i] ] = null;
}
}

var createBeforeAfterNodeSetter;

WrappedRange = function(range) {
Expand All @@ -61,7 +53,7 @@ rangy.createCoreModule("WrappedRange", ["DomRange"], function(api, module) {
updateRangeProperties(this);
};

DomRange.createPrototypeRange(WrappedRange, updateNativeRange, detach);
DomRange.createPrototypeRange(WrappedRange, updateNativeRange);

rangeProto = WrappedRange.prototype;

Expand Down
34 changes: 22 additions & 12 deletions test/rangetests.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ function testRangeCreator(docs, docName, rangeCreator, rangeCreatorName) {
xn.test.suite(rangeCreatorName + " in " + docName + " document", function(s) {
var doc;
var DOMException = rangy.DOMException;
var RangeException = rangy.RangeException;
var testRange = rangeCreator(document);

s.setUp = function(t) {
Expand Down Expand Up @@ -180,9 +179,10 @@ function testRangeCreator(docs, docName, rangeCreator, rangeCreatorName) {

range.detach();

testExceptionCode(t, function() {
// Detach is now a no-op according to DOM4. Not according to Chrome 35 though.
t.assertNoError(function() {
range[methodName](t.nodes.div2, 0);
}, DOMException.prototype.INVALID_STATE_ERR);
});
});

s.test(methodName + " move to other document test", function(t) {
Expand All @@ -205,17 +205,18 @@ function testRangeCreator(docs, docName, rangeCreator, rangeCreatorName) {

testExceptionCode(t, function() {
range[methodName](doc);
}, RangeException.prototype.INVALID_NODE_TYPE_ERR);
}, DOMException.prototype.INVALID_NODE_TYPE_ERR);

testExceptionCode(t, function() {
range[methodName](doc.createDocumentFragment());
}, RangeException.prototype.INVALID_NODE_TYPE_ERR);
}, DOMException.prototype.INVALID_NODE_TYPE_ERR);

range.detach();

testExceptionCode(t, function() {
// Detach is now a no-op according to DOM4. Not according to Chrome 35 though.
t.assertNoError(function() {
range[methodName](t.nodes.div2);
}, DOMException.prototype.INVALID_STATE_ERR);
});
});
};

Expand Down Expand Up @@ -478,8 +479,10 @@ function testRangeCreator(docs, docName, rangeCreator, rangeCreatorName) {
var range = rangeCreator(doc);
range.setStartBefore(t.nodes.b);
range.collapse(true);
t.assert(range.intersectsNode(t.nodes.b, true));
t.assertFalse(range.intersectsNode(t.nodes.b, false));
if (range.intersectsNode.length == 2) {
t.assert(range.intersectsNode(t.nodes.b, true));
}
t.assertFalse(range.intersectsNode(t.nodes.b));
});

s.test("intersectsNode 3", function(t) {
Expand Down Expand Up @@ -1176,13 +1179,16 @@ function testAcid3(rangeCreator, rangeCreatorName) {
r.setEndBefore(doc);
msg = "no exception thrown for setEndBefore() the document itself";
} catch (e) {
/*
This section is now commented out in 2011 Acid3 update
if (e.BAD_BOUNDARYPOINTS_ERR != 1)
msg = 'not a RangeException';
else if (e.INVALID_NODE_TYPE_ERR != 2)
msg = 'RangeException has no INVALID_NODE_TYPE_ERR';
else if ("INVALID_ACCESS_ERR" in e)
msg = 'RangeException has DOMException constants';
else if (e.code != e.INVALID_NODE_TYPE_ERR)
else*/ if (e.code != e.INVALID_NODE_TYPE_ERR)
msg = 'wrong exception raised from setEndBefore()';
}
t.assert(msg == "", msg);
Expand Down Expand Up @@ -1269,6 +1275,8 @@ function testAcid3(rangeCreator, rangeCreatorName) {

s.test("Acid3 test 10: Ranges and Attribute Nodes", function(t) {
// test 10: Ranges and Attribute Nodes
// COMMENTED OUT FOR 2011 UPDATE - turns out instead of dropping Attr entirely, as Acid3 originally expected, the API is just being refactored
/*
var e = document.getElementById('test');
if (!e.getAttributeNode) {
return; // support for attribute nodes is optional in Acid3, because attribute nodes might be removed from DOM Core in the future.
Expand All @@ -1286,6 +1294,7 @@ function testAcid3(rangeCreator, rangeCreatorName) {
t.assertEquals(r.toString(), '', "extracting contents didn't empty attribute value; instead equals '" + r.toString() + "'");
t.assertEquals(e.getAttribute('id'), '', "extracting contents didn't change 'id' attribute to empty string");
e.id = 'test';
*/
});

s.test("Acid3 test 11: Ranges and Comments", function(t) {
Expand Down Expand Up @@ -1316,8 +1325,9 @@ function testAcid3(rangeCreator, rangeCreatorName) {
r.surroundContents(doc.createElement('a'));
msg = 'no exception raised';
} catch (e) {
if ('code' in e) msg += '; code = ' + e.code;
if (e.code == 1) msg = '';
// COMMENTED OUT FOR 2011 UPDATE - DOM Core changes the exception from RangeException.BAD_BOUNDARYPOINTS_ERR (1) to DOMException.INVALID_STATE_ERR (11)
/*if ('code' in e) msg += '; code = ' + e.code;
if (e.code == 1) */msg = '';
}
t.assert(msg == '', "when trying to surround two halves of comment: " + msg);
t.assertEquals(r.toString(), "", "comments returned text");
Expand Down
Loading

0 comments on commit d6026de

Please sign in to comment.