function TextHtml(id, service, contentId, allowCodeView) {
    this.id = id;
    this.status = 0;
    this.contentId = contentId;
    this.service = service;
    this.allowCodeView = allowCodeView;
    this.target = $(this.id);
    this.e = $(this.id + '__e');
    this.e.absolutize();
    this.e.clonePosition(this.target);
    this.e.style.width = (this.e.getWidth() + 16) + 'px';
    this.editor = $(this.id + '__editor');
    this.code = $(this.id + '__code');
    this.lines = $(this.id + '__lines');
    this.codeBox = $(this.id + '__codeBox');
    this.tools = $(this.id + '__tools');
    this.noTools = $(this.id + '__noTools');
    this.codeBox.observe('keydown', 
        this.TextareaOnKeyDown.bindAsEventListener(this));
    this.btnEdit = $(this.id + '__edit');
    this.btnSave = $(this.id + '__save');
    this.btnFinish = $(this.id + '__finish');
    this.btnCode = $(this.id + '__btn__code');
    this.btnHtml = $(this.id + '__btn__html');
    this.win = this.editor.contentWindow;
    this.content = null;
    this.contextMenu = new ContextMenu(this);
    this.lastSaved = '';
    if (!this.allowCodeView) $(this.id + '__views').hide();
    this.selection = new Selection(this);
    this.mode = 'html';
    this.newLineTags = ['ul', 'li', 'br', 'span', 'table', 'tr', 'td', 'h2'];
    this.linkSettingsLoaded = false;
    this.mediaSettingsLoaded = false;
    this.tableSettingsLoaded = false;
    this.beforePasteContent = null;
}
TextHtml._c = null;
TextHtml.prototype.Doc = function() { return this.win.document; }
TextHtml.prototype.Edit = function() {
    if (this.status == 0) { // initialize
        BA.Update(this.id + '__tools', { parameters: { 
            service: 'Ctrl.textHtml.functions', light: true, id: this.id }});
        this.status = 1;
    }
    this.btnEdit.hide();
    this.btnSave.show('inline');
    this.btnFinish.show('inline');
    this.lastSaved = this.target.innerHTML;
    doc = this.Doc();
    doc.open();    
    var s = '<html><head>' 
      + '<link rel="stylesheet" type="text/css" href="themes/base/skin.css" />'
      + '<link rel="stylesheet" type="text/css" href="' + SKIN_CSS + '" />'
      + '<link rel="stylesheet" type="text/css" href="' + THEME + '" />'
      + '</head><body id="' + this.id + '__content" class="editor textHtml">' 
      + this.target.innerHTML + '</body></html>'; 
    doc.write(s);
    doc.close();
    doc.designMode = 'On';
    this.content = doc.getElementById(this.id + '__content');
    //this.content = doc.getElementsByTagName('body')[0];
    this.codeBox.value = this.content.innerHTML;
    this.target.hide();
    Event.observe(this.content, 'mouseup', 
        this.OnSelect.bindAsEventListener(this));
    Event.observe(this.content, 'paste', 
        this.OnPaste.bindAsEventListener(this));
    this.e.show('block');
    this.e.style.height = this.editor.getHeight() + 'px';
    this.ShowHtml();
    //setTimeout('TextHtml.PrepareTables()', 500);       
}
TextHtml.prototype.Finish = function() {
    if (this.lastSaved != this.GetContent() && 
        !confirm(GetLabel('confirmFinishWithoutSaving'))) return;
    this.btnSave.hide();
    this.btnFinish.hide();
    this.btnEdit.show('inline');
    this.Doc().designMode = 'Off';
    this.e.hide();
    this.editor.hide();
    this.code.hide();
    this.target.innerHTML = this.lastSaved.replace(/%2B/g, '+');
    this.target.show('block');  
}
TextHtml.prototype.Save = function() {
    this.contextMenu.Hide();
    var c = this.GetContent();
    LoadingInfo.Show(GetLabel('SavingContent'));
    BA.Call({parameters:{service: this.service, id: this.contentId, content: c}, 
        onComplete: this.OnSaved.bindAsEventListener(this) });
}
TextHtml.prototype.ShowCode = function() {
    this.tools.hide();
    this.noTools.show('block');
    this.btnCode.addClassName('active').stopObserving('click');
    this.btnHtml.removeClassName('active').observe(
        'click', this.ShowHtml.bindAsEventListener(this));
    for (var i = 0; i < this.content.childNodes.length; i++) {
        var e = this.content.childNodes[i];
        if (e.nodeValue) e.nodeValue = e.nodeValue.trim();
    }
    var v = this.content.innerHTML;
    // add line breaks before opening and closing tags /////////////////////////
    for (var i = 0; i < this.newLineTags.length; i++) {
        var t = this.newLineTags[i];
        v = v.replace(new RegExp('<' + t, 'gi'), '\n<' + t);
        v = v.replace(new RegExp('</' + t, 'gi'), '\n</' + t);
    }
    // replace double line breaks //////////////////////////////////////////////
    v = v.replace(/\n\n/gi, '\n');    
    this.codeBox.value = v;
    this.codeBox.lines = this.codeBox.value.split('\n').length;
    this.editor.hide();
    var cl = this.codeBox.value.split('\n').length;
    this.lines.innerHTML = '';
    for (var i = 1; i <= cl; i++)
        this.lines.innerHTML += i > 1 ? '<br />' + i : i;
    this.codeBox.style.height = ((cl * 15) + 2) + 'px';
    this.lines.show('block');
    this.code.show('block');
    this.mode = 'code';
}
TextHtml.prototype.ShowHtml = function() {
    this.noTools.hide();
    this.tools.show('block');
    this.btnHtml.addClassName('active').stopObserving('click');
    this.btnCode.removeClassName('active').observe(
        'click', this.ShowCode.bindAsEventListener(this));
    this.content.innerHTML = this.codeBox.value.replace('%2B', '+');
    this.lines.hide();
    this.code.hide();
    this.editor.show('block');
    this.mode = 'html';
}
TextHtml.prototype.GetContent = function() {
    if (this.mode == 'html') {
        var tables = this.Doc().getElementsByTagName('table');
        for (var i = 0; i < tables.length; i++)
            tables[i].setAttribute('border', 0);
        var c = this.content.innerHTML.replace(/\n/gi, '').replace(
            /<BR>/gi,'<br />\n');
        for (var i = 0; i < tables.length; i++)
            tables[i].setAttribute('border', 1);
    }
    else 
        var c = this.codeBox.value; 
    c = c.replace(/(\+)/gi, '%2B');
    return c;   
}
TextHtml.prototype.ClearContent = function() {
    this.codeBox.value = '';
    this.content.innerHTML = '';
}
TextHtml.prototype.Set = function(cmd, args) {
    if (isIE) this.selection.GetSelection();
    switch (cmd) {
        case 'FormatBlock': 
            if (args != "") break;
            var p = this.selection.GetParentElement().parentNode;
            for (var i = 0; i < p.childNodes.length; i++) {
                var e = p.childNodes[i];
                if (e.nodeType != 1) continue;
                var tn = e.tagName.toLowerCase();
                if (tn == 'h1' || tn == 'h2' || tn == 'h3' || tn == 'p') 
                    e.parentNode.replaceChild(this.Doc().createTextNode(
                        " " + e.innerHTML + " "), e);
            }
            break;
    }
    this.Doc().execCommand(cmd, false, args);
    switch (cmd) {
        case 'CreateLink': this.ProcessLink(); break;
    }
}
TextHtml.prototype.HideSettings = function() {
    this.e.select('div[class~=settings]').invoke('hide');
}
// hyperlinks //////////////////////////////////////////////////////////////////
TextHtml.prototype.Link = function() {
    Dialog('linkSettings', { service: 'Ctrl.textHtml.linkSettings', light: false, id: this.id }, 
      this.OnLinkSettingsLoaded.bindAsEventListener(this));
}
TextHtml.prototype.OnLinkSettingsLoaded = function() {
    ShowDialog();
    var str = this.selection.GetSelection();
    
    if (/(www)/g.test(str)) {
        $(this.id + '__linkSettings__protocol').selectedIndex = 0;
        $(this.id + '__linkSettings__url').value = str;
    }
    // if selection contains @ we assume that it should be a mailto link
    if(/(@)/g.test(str)) {
        $(this.id + '__linkSettings__url').value = str;
        $(this.id + '__linkSettings__protocol').selectedIndex = 2;
    }
}
TextHtml.prototype.SetLink = function() {
    var url = ''; var p = this.id + '__linkSettings__';
    if ($F(p + 'type__external')) {
        protocol = $F(p + 'protocol');
        url = protocol + $F(p + 'url');
        if (protocol == 'mailto:') url += 'EXT_NML';
    }
    else if ($F(p + 'type__internal')) {
        url = 'index.php?tabid=' + $F(p + 'page');
    }
    else if ($F(p + 'type__mediaFile')) { 
        url = 'media/portals/1/documents/' + $F(p + 'mediaFiles'); 
    }
    else if ($F(p + 'type__historyBack')) {
        url = 'javascript:history.back();EXT_BACK';
    }
    else if ($F(p + 'type__anchor')) {
        url = '#BASEPageStartEXT_ANCHOR';
    }
    if ($F(p + 'target__blank')) url += 'EXT_BLANK';
    else if ($F(p + 'target__popup')) {
        url = "javascript:OpenDialog('" + url + "','" +
            escape($F(p + 'target__popup__windowName')) + 
            "'," + parseInt($F(p + 'target__popup__width')) + 
            "," + parseInt($F(p + 'target__popup__height')) + ");EXT_POPUP";
    }
    this.Set('CreateLink', url);
    $('dialog').hide();
}
TextHtml.prototype.ProcessLink = function() {
    this.content.innerHTML = this.content.innerHTML.replace(
      /(EXT_NML)/g, '" class="mail"');
    this.content.innerHTML = this.content.innerHTML.replace(
      /(EXT_BLANK)/g, '" target="_blank"');
    this.content.innerHTML = this.content.innerHTML.replace(
      /(EXT_BACK)/g, '" class="backlink"');
    this.content.innerHTML = this.content.innerHTML.replace(
      /(EXT_POPUP)/g, '" class="popup"');
    this.content.innerHTML = this.content.innerHTML.replace(
      /(EXT_ANCHOR)/g, '" class="anchor"');
}
// tables //////////////////////////////////////////////////////////////////////
TextHtml.prototype.PrepareTables = function() {
    this.TableEvents();
    Event.observe($(this.Doc().getElementsByTagName('div')[0]), 'click', 
        function() { $(this.id + '__contextMenu').hide(); });
    if (isIE) 
        this.Doc().attachEvent('onbeforedeactivate', 
            function() { this.selection.Save() });
}
TextHtml.prototype.RemoveTableEvents = function() { this.TableEvents(true); }
TextHtml.prototype.TableEvents = function(r) {
    var t = this.Doc().getElementsByTagName('table');
    for (var i = 0; i < tables.length; i++) {
        t[i].setAttribute('border', r === true ? 0 : 1);
        if (r === true) 
            Event.stopObserving(t[i], 'contextmenu', this.contextMenu.Show);
        else
            Event.observe(t[i], 'contextmenu', this.contextMenu.Show);  
    }
}
TextHtml.prototype.Table = function() {
    Dialog('tableSettings', { service: 'Ctrl.textHtml.tableSettings', light: true, id: this.id }, 
        this.OnTableSettingsLoaded.bindAsEventListener(this)); 
}
TextHtml.prototype.OnTableSettingsLoaded = function() {
    ShowDialog();
}
TextHtml.prototype.InsertTable = function() {
    var p = this.id + '__tableSettings__';
    var r = $F(p + 'rows'), c = $F(p + 'columns');
    var t = this.Doc().createElement('table');
    t.setAttribute('cellpadding', $F(p + 'cellPadding'));
    t.setAttribute('cellspacing', $F(p + 'cellSpacing'));
    t.setAttribute('border', 1);
    t.style.width = $F(p + 'width') + $F(p + 'widthUnit');
    t.style.height = $F(p + 'height') + 'px';
    var tb = this.Doc().createElement('tbody');
    for (var i = 0; i < r; i++) {
        var tr = this.Doc().createElement('tr');
        for (var j = 0; j < c; j++) {
            var td = this.Doc().createElement('td');
            td.innerHTML = '&nbsp;';
            tr.appendChild(td);
        }
        tb.appendChild(tr);
    }
    t.appendChild(tb);
    this.InsertNodeAtSelection(t);
    $(this.id + '__tableSettings').hide();
}
// media ///////////////////////////////////////////////////////////////////////
TextHtml.prototype.Media = function() {
    Dialog('mediaLibrary', { service: 'Ctrl.textHtml.mediaSettings', light: false, id: this.id }, 
        this.OnMediaSettingsLoaded.bindAsEventListener(this));  
}
TextHtml.prototype.OnMediaSettingsLoaded = function() {
    $(this.id + '__media').handler = new AjaxContainer(this.id + '__media', '', 'Ctrl.directoryView.load', { id : this.id + '__media' }, '');
    $(this.id + '__media').handler.Status('nodata');
    $(this.id + '__media').h = new DirectoryView(this.id + '__media');
    this.mediaSettingsLoaded = true;
    ShowDialog();
}
TextHtml.prototype.InsertMedia = function(id) {
    this.Set('InsertImage', 'media/portals/1/' + DirectoryView._path + $(DirectoryView._s).select('img')[0].alt);
    $('dialog').hide();
}
// code box functionalities ////////////////////////////////////////////////////
TextHtml.prototype.TextareaOnKeyDown = function(ev) {
    
    var e = Event.element(ev);
    switch (ev.keyCode) {
        case Event.KEY_RETURN : 
            this.codeBox.lines++;
            this.codeBox.addLineHeight();
            this.lines.innerHTML += '<br />' + this.codeBox.lines;
            break;
        case Event.KEY_DELETE:
        case Event.KEY_BACKSPACE:
            var a = this.codeBox.value.split('\n');
            var nl = a.length - 1;
            if (nl > 0 && nl < this.codeBox.lines && a[nl] == '') {
                this.codeBox.removeLineHeight(this.codeBox.lines - nl);
                this.lines.innerHTML = this.lines.innerHTML.split('<br>').slice(
                    0, nl).join('<br />');
                this.codeBox.lines = nl;
                if (ev.keyCode == Event.KEY_DELETE)
                    this.codeBox.value = a.slice(0, nl).join('\n');
            }
            break;
        default: this.codeBox.lines = this.codeBox.value.split('\n').length;
    }
}
// utilities ///////////////////////////////////////////////////////////////////
TextHtml.prototype.InsertNodeAtSelection = function(n) {
    var s = this.selection.GetSelection();
    if (!isIE) {
        var r = s.getRangeAt(0);
        s.removeAllRanges();
        r.deleteContents();
        var c = r.startContainer, p = r.startOffset;
        r = document.createRange();
        if (c.nodeType == 3 && n.nodeType == 3) {
            c.insertData(p, n.nodeValue);
            r.setEnd(c, p + n.length);
            r.setStart(c, p + n.length);
        }
        else {
            var an;
            if (c.nodeType == 3) {
                var tn = c;
                c = tn.parentNode;
                var txt = tn.nodeValue;
                var tb = txt.substr(0, p), ta = txt.substr(p);
                var bn = document.createTextNode(tb);
                an = document.createTextNode(ta);
                c.insertBefore(an, tn);
                c.insertBefore(n, an);
                c.insertBefore(bn, n);
                c.removeChild(tn);
            }
            else {
                an = c.childNodes[p];
                c.insertBefore(n, an);
            }
            r.setEnd(an, 0);
            r.setStart(an, 0);
        }
        s.addRange(r);
        Event.observe(n, 'contextmenu', this.contextMenu.Show);
    }
    else {
        var d = this.Doc().getElementsByTagName('div')[0];
        d.appendChild(n);
        n.attachEvent('oncontextmenu', 
            function(ev) { this.contextMenu.Show(ev); (ev)} );
    }
}
TextHtml.GetElementAscensor = function(e, n) {
    while (e) {
        if (e.nodeName.compareTo(n)) return e;
        e = e.parentNode;
    }
    return null;
}
TextHtml.GetElementDocument = function(e) {
    return e.ownerDocument || e.document;
}
// event handlers //////////////////////////////////////////////////////////////
TextHtml.prototype.OnSaved = function() {
    LoadingInfo.Hide();
    alert(GetLabel('ContentSaved'));
    this.lastSaved = this.GetContent();
}
TextHtml.prototype.OnSelect = function() {
    var e = $(this.selection.GetParentElement());
    if (e != null)
        $(this.id + '__formats').selectValue(e.tagName.toLowerCase());
}
TextHtml.prototype.OnPaste = function(e) {
    e.returnValue = false;
    TextHtml._c = this;
    setTimeout('TextHtml._c.OnPasted()', 500);
}
TextHtml.prototype.OnPasted = function() {    
}
// context menu ////////////////////////////////////////////////////////////////
ContextMenu = function(textHtml) { 
    this.e = $(textHtml.id + '__contextMenu'); 
    this.th = textHtml; 
};
ContextMenu.prototype.Show = function(ev) {
    this.e.show('block');
    this.e.moveToMouse(ev, this.editor);
    this.e.targetElement = Event.element(ev).parentNode.parentNode.parentNode;
    Event.stop(ev);
    return false;
}
ContextMenu.prototype.Hide = function() { if (this.e) this.e.hide(); }
ContextMenu.prototype.Init = function() {
    this.e.select('li').each(function(e) { 
        e.observe('mouseover', function() { e.addClassName('active'); (e)});
        e.observe('mouseout', function() { e.removeClassName('active'); (e)});
    });
}
ContextMenu.prototype.ShowSub = function(id) {
    this.HideSubs(); $(this.id + '__' + id).show('block');
}
ContextMenu.prototype.HideSubs = function() {
    this.e.select('ul[class=sub]').invoke('hide');
}
// table ///////////////////////////////////////////////////////////////////////
var Table = function(textHtml) { this.th = textHtml };
Table.ClearRow = function(r) {
    for (var i = 0; i < r.cells.length; i++) r.cells[i].innerHTML = '&nbsp;';
}
Table.prototype.Delete = function(t) {
    if (!t) t = this.th.GetSelectedElement('table');
    if (!t) return;
    this.th.selection.SelectNode(t);
    this.th.selection.Collapse();
    if (t.parentNode.childNodes.length == 1)
        t.parentNode.parentNode.removeChild(t.parentNode);
    else
        t.parentNode.removeChild(t);
}
Table.prototype.InsertRow = function(before) {
    var r = this.th.selection.MoveToAncestorNode('TR');
    if (!r) return;
    var nr = r.cloneNode(true);
    r.parentNode.insertBefore(nr, r);
    Table.ClearRow(before ? nr : r);
}
Table.prototype.DeleteRows = function(r) {
    if (!r) {
        var c = this.GetSelectedCells();
        var rtd = new Array();
        for (var i = 0; i < c.length; i++) {
            r = this.th.GetElementsAscensor(c[i], 'tr');
            rtd[r.rowIndex] = r;
        }
        for (var i = rtd.length; i >= 0; i--)
            if (rtd[i]) this.DeleteRows(rtd[i]);
    }
    var t = TextHtml.GetElementAscensor(r, 'table');
    if (t.rows.length == 1) return this.Delete(t);
    r.parentNode.removeChild(r);
    return false;
}
Table.prototype.InsertColumn = function(before) {
    var c = null, n = this.GetSelectedCells();
    if (n && n.length) c = n[before ? 0 : n.length - 1];
    if (!c) return;
    var index = c.cellIndex, t = TextHtml.GetElementAscensor(c, 'table');
    for (var i = 0; i < t.rows.length; i++) {
        var r = t.rows[i];
        if (r.cells.length < index + 1) continue;
        c = r.cells[index].cloneNode(false);
        var bc = r.cells[index];
        if (before) r.insertBefore(c, bc);
        else if (bc.nextSibling) r.insertBefore(c, bc.nextSibling);
        else r.appendChild(c);
    }
}
Table.prototype.DeleteColumns = function(c) {
    if (!c) {
        var ctd = this.GetSelectedCells();
        for (var i = ctd.length; i >= 0; i--)
            if (ctd[i]) this.DeleteColumns(ctd[i]);
        return;
    }
    var t = this.th.GetElementsAscensor(c, 'table'), index = c.cellIndex;
    for (var i = t.rows.length - 1; i >= 0; i--) {
        var r = t.rows[i];
        if (index == 0 && r.cells.length == 1) {
            this.DeleteRows(r);
            continue;
        }
        if (r.cells[index]) r.removeChild(r.cells[index]);
    }
}
Table.prototype.GetSelectedCells = function() {
    if (isIE && this.th.selection.GetType() == 'Control') {
        td = this.th.selection.MoveToAncestorNode('td');
        return td ? [td] : [];
    }
    var c = [], s = this.th.selection.GetSelection();
    if (isIE) {
        var r = s.createRange(), p = this.th.selection.GetParentElement();
        if (p && p.tagName.compareTo('td')) c[0] = p;
        else {
            p = this.th.selection.MoveToAncestorNode('table');
            if (p) {
                for (var i = 0; i < p.cells.length; i++) {
                    var cr = this.th.Doc().body.createTextRange();
                    cr.moveToElementText(p.cells[i]);
                    if (r.inRange(cr) || 
                        (r.compareEndPoints('StartToStart', cr) >= 0 && 
                            r.compareEndPoints('StartToEnd', cr) <= 0) || 
                        (r.compareEndPoints('EndToStart', cr) >= 0 && 
                            r.compareEndPoints('EndToEnd', cr) <= 0))
                        c[c.length] = p.cells[i]; 
                }
            }
        }
    }
    else {
        if (s.rangeCount == 1 && s.anchorNode.nodeType == 3) {
            var p = TextHtml.GetElementAscensor(s.anchorNode, 'td');
            if (p) c[0] = p;
            return c;
        }
        for (var i = 0; i < s.rangeCount; i++) {
            var c, r = s.getRangeAt(i), sc = r.startContainer;
            c = sc.tagName.compareTo('td') ? sc : sc.childNodes[r.startOffset];
            if (c.tagName.compareTo('td')) c[c.length] = c;
        }
    }
    return c;
}
// selection ///////////////////////////////////////////////////////////////////
function Selection(textHtml) {
    this.th = textHtml;
    this.selectionData = null;
    this.selectionChangeLocked = false;
}
Selection.prototype.GetSelection = function() {
    if (isIE) {
        this.Restore();
        s = this.th.win.selection;
    }
    else s = this.th.win.getSelection();
    return s;
}
Selection.prototype.Save = function() {
    if (!this.editorDocument) return;
    var r, s = this.th.win.selection;
    if (s) {
        r = s.createRange();
        if (r && (r.parentElement() && TextHtml.GetElementDoc(r.parentElement() != self.editorDocument) 
            || (r.item && TextHtml.GetElementDocument(r.item(0)) != self.editorDocument))) r = null;
    }
    this.selectionData = r;
}
Selection.prototype.Release = function() { delete this.selectionData; }
Selection.prototype.Restore = function() {
    if (!this.selectionData) return;
    this.selectionChangeLocked = true;
    try {
        if (String(this.GetSelectionDoc(
            self.editorDocument.selection).body.contentEditable) == 'true') {
            this.selectionChangeLocked = false;
            return;
        }
        this.selectionData.select();
    } catch (e) {}
    this.selectionChangeLocked = false;
}
Selection.prototype.GetSelectionDocument = function(s) {
    var r = s.createRange();
    return r ? TextHtml.GetElementDocument(
        r.item ? r.item(0) : r.parentElement()) : null;
}
Selection.prototype.GetParentElement = function() {
    this.GetSelection();
    switch (this.GetType()) {
        case 'Control': 
            var e = this.GetSelectedElement(); 
            return e ? e.parentElement() : null;
        case 'None': return null;
        default: 
            return this.GetSelection().anchorNode.parentNode;
    }
}
Selection.prototype.GetType = function() {
    try {
        var ieType = this.GetSelection().type;
        if (ieType == 'Control' || ieType == 'Text') return ieType;
        if (this.GetSelection().anchorNode) return 'Text';
    } catch (e) {}
    return 'None';
}
Selection.prototype.GetSelectedElements = function(tn) {
    var s = this.th.win && this.GetSelection();
    if (!s || s.rangeCount < 1) return null;
    var r = s.getRangeAt(0);
    if (r.startContainer != r.endContainer || r.startContainer.nodeType != 1 || 
        r.startOffset != r.endOffset - 1) return null;
    var e = r.startContainer.childNodes[r.startOffset];
    if (e.nodeType != 1) return null;
    if (!(e && e.tagName.compareTo(tn))) e = this.MoveToAncestorNode(tn);
    return e;
}
Selection.prototype.MoveToAncestorNode = function(tn) {
    if (isIE) {
        if (!this.th.win) return null;
        var n, s = this.GetSelection(), r = s.createRange();
        if (s.type == 'Control') {
            for (var i = 0; i < r.length; i++)
                if (r(i).parentNode) { n = r(i).parentNode; break; }
        }
        else n = r.parentElement();
        while (n && !n.nodeName.compareTo(tn)) n = n.parentNode;
        return n;
    }
    else {
        var c = this.GetSelectedElement(tn);
        if (!c) c = this.GetSelection().getRangeAt(0).startContainer;
        while (c) {
            if (c.nodeName.compareTo(tn)) return c;
            c = c.parentNode;
        }
        return null;
    }
}
Selection.prototype.SelectNode = function(n) {
    var r = this.th.win.createRange();
    r.selectNode(n);
    var s = this.GetSelection();
    s.removeAllRanges();
    s.addRange(r);
}
Selection.prototype.Collapse = function(toStart) {
    var s = this.GetSelection();
    if (toStart == null || toStart === true) s.collpaseToStart();
    else s.collapseToEnd();
}

