blob: f690f6f24a6ebea814e3d1e3012dec17eb71254e [file] [log] [blame]
/**
* @class w3cRange - w3c text range emulation for "strange" browsers
*
* @param elRTE rte объект-редактор
*
* @author: Dmitry Levashov (dio) dio@std42.ru
* Copyright: Studio 42, http://www.std42.ru
**/
(function($) {
elRTE.prototype.w3cRange = function(rte) {
var self = this;
this.rte = rte;
this.r = null;
this.collapsed = true;
this.startContainer = null;
this.endContainer = null;
this.startOffset = 0;
this.endOffset = 0;
this.commonAncestorContainer = null;
this.range = function() {
try {
this.r = this.rte.window.document.selection.createRange();
} catch(e) {
this.r = this.rte.doc.body.createTextRange();
}
return this.r;
}
this.insertNode = function(html) {
this.range();
self.r.collapse(false)
var r = self.r.duplicate();
r.pasteHTML(html);
}
this.getBookmark = function() {
this.range();
if (this.r.item) {
var n = this.r.item(0);
this.r = this.rte.doc.body.createTextRange();
this.r.moveToElementText(n);
}
return this.r.getBookmark();
}
this.moveToBookmark = function(bm) {
this.rte.window.focus();
this.range().moveToBookmark(bm);
this.r.select();
}
/**
* Обновляет данные о выделенных нодах
*
* @return void
**/
this.update = function() {
function _findPos(start) {
var marker = '\uFEFF';
var ndx = offset = 0;
var r = self.r.duplicate();
r.collapse(start);
var p = r.parentElement();
if (!p || p.nodeName == 'HTML') {
return {parent : self.rte.doc.body, ndx : ndx, offset : offset};
}
r.pasteHTML(marker);
childs = p.childNodes;
for (var i=0; i < childs.length; i++) {
var n = childs[i];
if (i>0 && (n.nodeType!==3 || childs[i-1].nodeType !==3)) {
ndx++;
}
if (n.nodeType !== 3) {
offset = 0;
} else {
var pos = n.nodeValue.indexOf(marker);
if (pos !== -1) {
offset += pos;
break;
}
offset += n.nodeValue.length;
}
};
r.moveStart('character', -1);
r.text = '';
return {parent : p, ndx : Math.min(ndx, p.childNodes.length-1), offset : offset};
}
this.range();
this.startContainer = this.endContainer = null;
if (this.r.item) {
this.collapsed = false;
var i = this.r.item(0);
this.setStart(i.parentNode, this.rte.dom.indexOf(i));
this.setEnd(i.parentNode, this.startOffset+1);
} else {
this.collapsed = this.r.boundingWidth == 0;
var start = _findPos(true);
var end = _findPos(false);
start.parent.normalize();
end.parent.normalize();
start.ndx = Math.min(start.ndx, start.parent.childNodes.length-1);
end.ndx = Math.min(end.ndx, end.parent.childNodes.length-1);
if (start.parent.childNodes[start.ndx].nodeType && start.parent.childNodes[start.ndx].nodeType == 1) {
this.setStart(start.parent, start.ndx);
} else {
this.setStart(start.parent.childNodes[start.ndx], start.offset);
}
if (end.parent.childNodes[end.ndx].nodeType && end.parent.childNodes[end.ndx].nodeType == 1) {
this.setEnd(end.parent, end.ndx);
} else {
this.setEnd(end.parent.childNodes[end.ndx], end.offset);
}
// this.dump();
this.select();
}
return this;
}
this.isCollapsed = function() {
this.range();
this.collapsed = this.r.item ? false : this.r.boundingWidth == 0;
return this.collapsed;
}
/**
* "Схлопывает" выделение
*
* @param bool toStart - схлопывать выделение к началу или к концу
* @return void
**/
this.collapse = function(toStart) {
this.range();
if (this.r.item) {
var n = this.r.item(0);
this.r = this.rte.doc.body.createTextRange();
this.r.moveToElementText(n);
}
this.r.collapse(toStart);
this.r.select();
this.collapsed = true;
}
this.getStart = function() {
this.range();
if (this.r.item) {
return this.r.item(0);
}
var r = this.r.duplicate();
r.collapse(true);
var s = r.parentElement();
return s && s.nodeName == 'BODY' ? s.firstChild : s;
}
this.getEnd = function() {
this.range();
if (this.r.item) {
return this.r.item(0);
}
var r = this.r.duplicate();
r.collapse(false);
var e = r.parentElement();
return e && e.nodeName == 'BODY' ? e.lastChild : e;
}
/**
* Устанавливает начaло выделения на указаную ноду
*
* @param Element node нода
* @param Number offset отступ от начала ноды
* @return void
**/
this.setStart = function(node, offset) {
this.startContainer = node;
this.startOffset = offset;
if (this.endContainer) {
this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
}
}
/**
* Устанавливает конец выделения на указаную ноду
*
* @param Element node нода
* @param Number offset отступ от конца ноды
* @return void
**/
this.setEnd = function(node, offset) {
this.endContainer = node;
this.endOffset = offset;
if (this.startContainer) {
this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
}
}
/**
* Устанавливает начaло выделения перед указаной нодой
*
* @param Element node нода
* @return void
**/
this.setStartBefore = function(n) {
if (n.parentNode) {
this.setStart(n.parentNode, this.rte.dom.indexOf(n));
}
}
/**
* Устанавливает начaло выделения после указаной ноды
*
* @param Element node нода
* @return void
**/
this.setStartAfter = function(n) {
if (n.parentNode) {
this.setStart(n.parentNode, this.rte.dom.indexOf(n)+1);
}
}
/**
* Устанавливает конец выделения перед указаной нодой
*
* @param Element node нода
* @return void
**/
this.setEndBefore = function(n) {
if (n.parentNode) {
this.setEnd(n.parentNode, this.rte.dom.indexOf(n));
}
}
/**
* Устанавливает конец выделения после указаной ноды
*
* @param Element node нода
* @return void
**/
this.setEndAfter = function(n) {
if (n.parentNode) {
this.setEnd(n.parentNode, this.rte.dom.indexOf(n)+1);
}
}
/**
* Устанавливает новое выделение после изменений
*
* @return void
**/
this.select = function() {
// thanks tinymice authors
function getPos(n, o) {
if (n.nodeType != 3) {
return -1;
}
var c ='\uFEFF';
var val = n.nodeValue;
var r = self.rte.doc.body.createTextRange();
n.nodeValue = val.substring(0, o) + c + val.substring(o);
r.moveToElementText(n.parentNode);
r.findText(c);
var p = Math.abs(r.moveStart('character', -0xFFFFF));
n.nodeValue = val;
return p;
};
this.r = this.rte.doc.body.createTextRange();
var so = this.startOffset;
var eo = this.endOffset;
var s = this.startContainer.nodeType == 1
? this.startContainer.childNodes[Math.min(so, this.startContainer.childNodes.length - 1)]
: this.startContainer;
var e = this.endContainer.nodeType == 1
? this.endContainer.childNodes[Math.min(so == eo ? eo : eo - 1, this.endContainer.childNodes.length - 1)]
: this.endContainer;
if (this.collapsed) {
if (s.nodeType == 3) {
var p = getPos(s, so);
this.r.move('character', p);
} else {
this.r.moveToElementText(s);
this.r.collapse(true);
}
} else {
var r = this.rte.doc.body.createTextRange();
var sp = getPos(s, so);
var ep = getPos(e, eo);
if (s.nodeType == 3) {
this.r.move('character', sp);
} else {
this.r.moveToElementText(s);
}
if (e.nodeType == 3) {
r.move('character', ep);
} else {
r.moveToElementText(e);
}
this.r.setEndPoint('EndToEnd', r);
}
try {
this.r.select();
} catch(e) {
}
if (r) {
r = null;
}
}
this.dump = function() {
this.rte.log('collapsed: '+this.collapsed);
//this.rte.log('commonAncestorContainer: '+this.commonAncestorContainer.nodeName||'#text')
this.rte.log('startContainer: '+(this.startContainer ? this.startContainer.nodeName : 'non'));
this.rte.log('startOffset: '+this.startOffset);
this.rte.log('endContainer: '+(this.endContainer ? this.endContainer.nodeName : 'none'));
this.rte.log('endOffset: '+this.endOffset);
}
}
})(jQuery);