InternetExplorerRange.js
Summary
Provides a W3C Range implementation under Internet Explorer.
History: The original code was written by Jorgen Horstink (http://jorgenhorstink.nl/2006/03/11/w3c-range-in-internet-explorer/).
It was extensively modified by David Kingma.
This version has been adapted for use with Mozile by James A. Overton.
Key changes include wrapping the objects in the Mozile namespace, so as to minimize the impact on other scripts in the same page.
Version: 0.8
$Id: overview-summary-InternetExplorerRange.js.html,v 1.9 2006/08/23 23:30:17 jameso Exp $
Author: James A. Overton
mozile.require("mozile.dom");
mozile.require("mozile.xpath");
mozile.provide("mozile.dom.InternetExplorerRange");
mozile.dom.InternetExplorerRange = function(range) {
this._range = null;
if(range)
this._range = range;
else {
this._range = document.body.createTextRange();
}
this.collapsed = null;
this.commonAncestorContainer = null;
this.startContainer = null;
this.startOffset = null;
this.endContainer = null;
this.endOffset = null;
}
mozile.dom.InternetExplorerRange.prototype._init = function () {
var beginRange = this._range.duplicate();
beginRange.collapse(true);
var position = this._getPosition(beginRange);
this.startContainer = position.node;
this.startOffset = position.offset;
var endRange = this._range.duplicate();
endRange.collapse(false);
position = this._getPosition(endRange);
this.endContainer = position.node;
this.endOffset = position.offset;
this._commonAncestorContainer();
this._collapsed();
}
mozile.dom.InternetExplorerRange.prototype._getPosition = function(textRange) {
var element = textRange.parentElement();
var range = document.body.createTextRange();
range.moveToElementText(element);
range.setEndPoint("EndToStart", textRange);
var rangeLength = range.text.length;
if(rangeLength < element.innerText.length / 2) {
var direction = 1;
var node = element.firstChild;
}
else {
direction = -1;
node = element.lastChild;
range.moveToElementText(element);
range.setEndPoint("StartToStart", textRange);
rangeLength = range.text.length;
}
while(node) {
switch(node.nodeType) {
case mozile.dom.TEXT_NODE:
nodeLength = node.data.length;
if(nodeLength < rangeLength) {
var difference = rangeLength - nodeLength;
if(direction == 1) range.moveStart("character", difference);
else range.moveEnd("character", -difference);
rangeLength = difference;
}
else {
if(direction == 1) return {node: node, offset: rangeLength};
else return {node: node, offset: nodeLength - rangeLength};
}
break;
case mozile.dom.ELEMENT_NODE:
nodeLength = node.innerText.length;
if(direction == 1) range.moveStart("character", nodeLength);
else range.moveEnd("character", -nodeLength);
rangeLength = rangeLength - nodeLength;
break;
}
if(direction == 1) node = node.nextSibling;
else node = node.previousSibling;
}
return {node: element, offset: 0};
}
mozile.dom.InternetExplorerRange.prototype._getOffset = function (startNode, startOffset) {
var node, moveCharacters;
if(startNode.nodeType == mozile.dom.TEXT_NODE) {
moveCharacters = startOffset;
node = startNode.previousSibling;
}
else if(startNode.nodeType == mozile.dom.ELEMENT_NODE) {
moveCharacters = 0;
if(startOffset > 0) node = startNode.childNodes[startOffset - 1];
else return 0;
}
else {
mozile.debug.inform("mozile.dom.InternetExplorerRange.prototype._getOffset", "Bad node given: "+ mozile.xpath.getXPath(startNode));
return 0;
}
while (node) {
var nodeLength = 0;
if(node.nodeType == mozile.dom.ELEMENT_NODE) {
nodeLength = node.innerText.length;
if(this._isChildless(node)) nodeLength = 1;
if(this._isBlock(node)) nodeLength++;
}
else if(node.nodeType == mozile.dom.TEXT_NODE) {
nodeLength = node.data.length;
}
moveCharacters += nodeLength;
node = node.previousSibling;
}
return moveCharacters;
}
mozile.dom.InternetExplorerRange.prototype._isBlock = function(node) {
switch (node.nodeName.toLowerCase()) {
case 'p':
case 'div':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
case 'pre':
return true;
}
return false;
}
mozile.dom.InternetExplorerRange.prototype._isChildless = function(node) {
switch (node.nodeName.toLowerCase()) {
case 'img':
case 'br':
case 'hr':
return true;
}
return false;
}
mozile.dom.InternetExplorerRange.prototype.setStart = function (startNode, startOffset) {
var container = startNode;
if(startNode.nodeType == mozile.dom.TEXT_NODE ||
startNode.nodeType == mozile.dom.COMMENT_NODE ||
startNode.nodeType == mozile.dom.CDATA_SECTION_NODE) {
container = container.parentNode;
}
var copyRange = this._range.duplicate();
copyRange.moveToElementText(container);
copyRange.collapse(true);
copyRange.move('Character', this._getOffset(startNode, startOffset));
this._range.setEndPoint('StartToStart', copyRange);
this.startContainer = startNode;
this.startOffset = startOffset;
if (this.endContainer == null && this.endOffset == null) {
this.endContainer = startNode;
this.endOffset = startOffset;
}
this._commonAncestorContainer();
this._collapsed();
}
mozile.dom.InternetExplorerRange.prototype.setEnd = function (endNode, endOffset) {
var copyRange = this._range.duplicate();
copyRange.collapse(true);
var container = endNode;
if(endNode.nodeType == mozile.dom.TEXT_NODE ||
endNode.nodeType == mozile.dom.COMMENT_NODE ||
endNode.nodeType == mozile.dom.CDATA_SECTION_NODE) {
container = container.parentNode;
}
var copyRange = this._range.duplicate();
copyRange.moveToElementText(container);
copyRange.collapse(true);
copyRange.move('Character', this._getOffset(endNode, endOffset));
this._range.setEndPoint('EndToEnd', copyRange);
this.endContainer = endNode;
this.endOffset = endOffset;
if (this.startContainer == null && this.startOffset == null) {
this.startContainer = endNode;
this.startOffset = endOffset;
}
this._commonAncestorContainer();
this._collapsed();
}
mozile.dom.InternetExplorerRange.prototype.setStartBefore = function (referenceNode) {
this.setStart(referenceNode.parentNode, mozile.dom.getIndex(referenceNode));
};
mozile.dom.InternetExplorerRange.prototype.setStartAfter = function (referenceNode) {
this.setStart(referenceNode.parentNode, mozile.dom.getIndex(referenceNode) + 1);
};
mozile.dom.InternetExplorerRange.prototype.setEndBefore = function (referenceNode) {
this.setEnd(referenceNode.parentNode, mozile.dom.getIndex(referenceNode));
};
mozile.dom.InternetExplorerRange.prototype.setEndAfter = function (referenceNode) {
this.setEnd(referenceNode.parentNode, mozile.dom.getIndex(referenceNode) + 1);
};
mozile.dom.InternetExplorerRange.prototype.selectNode = function (referenceNode) {
this.setStartBefore(referenceNode);
this.setEndAfter(referenceNode);
};
mozile.dom.InternetExplorerRange.prototype.selectNodeContents = function (referenceNode) {
this.setStart(referenceNode, 0);
if(referenceNode.nodeType == mozile.dom.TEXT_NODE)
this.setEnd(referenceNode, referenceNode.data.length);
else
this.setEnd(referenceNode, referenceNode.childNodes.length);
};
mozile.dom.InternetExplorerRange.prototype.collapse = function (toStart) {
this._range.collapse(toStart);
if(toStart) {
this.endContainer = this.startContainer;
this.endOffset = this.startOffset;
} else {
this.startContainer = this.endContainer;
this.startOffset = this.endOffset;
}
this._commonAncestorContainer();
this._collapsed();
};
mozile.dom.InternetExplorerRange.prototype.cloneContents = function () {
var df = document.createDocumentFragment();
var container = this.commonAncestorContainer;
if(container.nodeType == mozile.dom.TEXT_NODE) {
df.appendChild(document.createTextNode(this._range.text));
return df;
}
var startNode = this.startContainer;
if(this.startContainer.nodeType != mozile.dom.TEXT_NODE)
startNode = this.startContainer.childNodes[this.startOffset];
var endNode = this.endContainer;
if(this.endContainer.nodeType != mozile.dom.TEXT_NODE)
endNode = this.endContainer.childNodes[this.endOffset - 1];
if(startNode == endNode) {
df.appendChild(startNode.cloneNode(true));
return df;
}
var current = container.firstChild;
var parent = null;
var clone;
while(current) {
if(!parent) {
if(mozile.dom.isAncestorOf(current, startNode, container)) {
parent = df;
}
else {
current = current.nextSibling;
continue;
}
}
if(current == startNode && this.startContainer.nodeType == mozile.dom.TEXT_NODE) {
content = this.startContainer.data.substring(this.startOffset);
parent.appendChild(document.createTextNode(content));
}
else if(current == endNode) {
if(this.endContainer.nodeType == mozile.dom.TEXT_NODE) {
content = this.endContainer.data.substring(0, this.endOffset);
parent.appendChild(document.createTextNode(content));
}
else parent.appendChild(endNode.cloneNode(false));
break;
}
else {
clone = current.cloneNode(false);
parent.appendChild(clone);
}
if(current.firstChild) {
parent = clone;
current = current.firstChild;
}
else if(current.nextSibling) {
current = current.nextSibling;
}
else while(current) {
if(current.parentNode) {
parent = parent.parentNode;
current = current.parentNode;
if(current.nextSibling) {
current = current.nextSibling;
break;
}
}
else current = null;
}
}
return df;
};
mozile.dom.InternetExplorerRange.prototype.deleteContents = function () {
this._range.pasteHTML('');
this.endContainer = this.startContainer;
this.endOffset = this.startOffset;
this._commonAncestorContainer();
this._collapsed();
};
mozile.dom.InternetExplorerRange.prototype.extractContents = function () {
var fragment = this.cloneContents();
this.deleteContents();
return fragment;
};
mozile.dom.InternetExplorerRange.prototype.insertNode = function (newNode) {
if(this.startContainer.nodeType == mozile.dom.TEXT_NODE){
this.startContainer.splitText(this.startOffset);
this.startContainer.parentNode.insertBefore(newNode, this.startContainer.nextSibling);
this.setStart(this.startContainer, this.startOffset);
return;
} else {
var parentNode = this.startContainer.parentNode;
if(this.startContainer.childNodes.length == this.startOffset) {
parentNode.appendChild(newNode);
}else {
this.startContainer.insertBefore(newNode, this.startContainer.childNodes.item(this.startOffset));
this.setStart(this.startContainer, this.startOffset+1);
return;
}
}
};
mozile.dom.InternetExplorerRange.prototype.surroundContents = function (newNode) {
newNode.appendChild(this.extractContents());
this.insertNode(newNode);
};
mozile.dom.InternetExplorerRange.prototype.compareBoundaryPoints = function (how, sourceRange) {
alert('mozile.dom.InternetExplorerRange.compareBoundaryPoints() is not implemented yet');
};
mozile.dom.InternetExplorerRange.prototype.cloneRange = function () {
var r = new mozile.dom.InternetExplorerRange(this._range.duplicate());
var properties = ["startContainer", "startOffset", "endContainer", "endOffset", "commonAncestorContainer", "collapsed"];
for(var i=0; i < properties.length; i++) {
r[properties[i]] = this[properties[i]];
}
return r;
};
mozile.dom.InternetExplorerRange.prototype.detach = function () {};
mozile.dom.InternetExplorerRange.prototype.toString = function () {
return this._range.text;
};
mozile.dom.InternetExplorerRange.prototype._commonAncestorContainer = function () {
if(this.startContainer == null || this.endContainer == null){
this.commonAncestorContainer = null;
return;
}
if(this.startContainer == this.endContainer) {
this.commonAncestorContainer = this.startContainer;
}
else {
this.commonAncestorContainer = mozile.dom.getCommonAncestor(this.startContainer, this.endContainer);
}
}
mozile.dom.InternetExplorerRange.prototype._collapsed = function () {
this.collapsed = (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
}
mozile.dom.InternetExplorerRange.prototype.store = mozile.dom.Range.prototype.store;
mozile.dom.InternetExplorerRange.prototype.restore = mozile.dom.Range.prototype.restore;
Documentation generated by
JSDoc on Wed Aug 23 18:45:51 2006