[FEATURE] RTE: Restructure methods dealing with nodes and selections
authorStanislas Rolland <typo3@sjbr.ca>
Fri, 20 Jan 2012 22:51:52 +0000 (17:51 -0500)
committerStanislas Rolland <typo3@sjbr.ca>
Mon, 23 Jan 2012 06:30:28 +0000 (07:30 +0100)
Note: This completes re-structuring of legacy htmlArea code.

Change-Id: I84cb9c1100c626b5b0f8938ace340b9072bf84f9
Resolves: #33372
Releases: 4.7
Reviewed-on: http://review.typo3.org/8646
Reviewed-by: Stanislas Rolland
Tested-by: Stanislas Rolland
36 files changed:
typo3/sysext/rtehtmlarea/class.tx_rtehtmlarea_base.php
typo3/sysext/rtehtmlarea/extensions/Language/class.tx_rtehtmlarea_language.php
typo3/sysext/rtehtmlarea/extensions/SelectFont/class.tx_rtehtmlarea_selectfont.php
typo3/sysext/rtehtmlarea/htmlarea/htmlarea-gecko.js [deleted file]
typo3/sysext/rtehtmlarea/htmlarea/htmlarea-ie.js [deleted file]
typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/Acronym/acronym.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/BlockElements/block-elements.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/BlockStyle/block-style.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/CharacterMap/character-map.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/ContextMenu/context-menu.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/CopyPaste/copy-paste.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultClean/default-clean.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultImage/default-image.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultInline/default-inline.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultLink/default-link.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefinitionList/definition-list.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/EditElement/edit-element.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/FindReplace/find-replace.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/InlineElements/inline-elements.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/InsertSmiley/insert-smiley.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/Language/language.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/PlainText/plain-text.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/QuickTag/quick-tag.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/RemoveFormat/remove-format.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/SelectFont/select-font.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3Color/typo3color.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3HtmlParser/typo3html-parser.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3Image/typo3image.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3Link/typo3link.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TableOperations/table-operations.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TextIndicator/text-indicator.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TextStyle/text-style.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/UndoRedo/undo-redo.js
typo3/sysext/rtehtmlarea/mod5/class.tx_rtehtmlarea_user.php
typo3/sysext/rtehtmlarea/pi2/class.tx_rtehtmlarea_pi2.php

index 5a284b4..729eb64 100644 (file)
@@ -745,11 +745,6 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
         */
        protected function addRteJsFiles($RTEcounter) {
                $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/htmlarea/htmlarea.js'));
-               if ($this->client['browser'] == 'msie') {
-                       $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/htmlarea/htmlarea-ie.js'));
-               } else {
-                       $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $this->ID . '/htmlarea/htmlarea-gecko.js'));
-               }
                foreach ($this->pluginEnabledCumulativeArray[$RTEcounter] as $pluginId) {
                        $extensionKey = is_object($this->registeredPlugins[$pluginId]) ? $this->registeredPlugins[$pluginId]->getExtensionKey() : $this->ID;
                        $this->pageRenderer->addJsFile($this->getFullFileName('EXT:' . $extensionKey . '/htmlarea/plugins/' . $pluginId . '/' . strtolower(preg_replace('/([a-z])([A-Z])([a-z])/', "$1".'-'."$2"."$3", $pluginId)) . '.js'));
@@ -1034,7 +1029,7 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
                }
                        // Include JS arrays of configured classes
                $configureRTEInJavascriptString .= '
-                       RTEarea[editornumber].classesUrl = "' . $this->writeTemporaryFile('', 'classes_' . $this->language, 'js', $this->buildJSClassesArray(), TRUE) . '";';
+                       RTEarea[editornumber].classesUrl = "' . (($this->is_FE() && $GLOBALS['TSFE']->absRefPrefix) ? $GLOBALS['TSFE']->absRefPrefix : '') . $this->writeTemporaryFile('', 'classes_' . $this->language, 'js', $this->buildJSClassesArray(), TRUE) . '";';
                return $configureRTEInJavascriptString;
        }
 
@@ -1156,7 +1151,7 @@ class tx_rtehtmlarea_base extends t3lib_rteapi {
                        }
                }
                if ($this->is_FE()) {
-                       $filename =  ($GLOBALS['TSFE']->absRefPrefix ? $GLOBALS['TSFE']->absRefPrefix : '') . $relativeFilename;
+                       $filename = $relativeFilename;
                } else {
                        $filename = ($this->isFrontendEditActive() ? '' : ($this->backPath . '../')) . $relativeFilename;
                }
index 31eabc6..232d834 100644 (file)
@@ -71,7 +71,7 @@ class tx_rtehtmlarea_language extends tx_rtehtmlarea_api {
                $registerRTEinJavascriptString = '';
                if (!is_array( $this->thisConfig['buttons.']) || !is_array($this->thisConfig['buttons.'][$button . '.'])) {
                        $registerRTEinJavascriptString .= '
-               RTEarea['.$RTEcounter.'].buttons.'. $button .' = new Object();';
+                       RTEarea['.$RTEcounter.'].buttons.'. $button .' = new Object();';
                }
                if ($this->htmlAreaRTE->is_FE()) {
                        $first = $GLOBALS['TSFE']->getLLL('No language mark',$this->LOCAL_LANG);
@@ -91,7 +91,7 @@ class tx_rtehtmlarea_language extends tx_rtehtmlarea_api {
                }
                $languagesJSArray = json_encode(array('options' => $languagesJSArray));
                $registerRTEinJavascriptString .= '
-       RTEarea['.$RTEcounter.'].buttons.'. $button .'.dataUrl = "' . $this->htmlAreaRTE->writeTemporaryFile('', $button . '_' . $this->htmlAreaRTE->contentLanguageUid, 'js', $languagesJSArray) . '";';
+                       RTEarea['.$RTEcounter.'].buttons.'. $button .'.dataUrl = "' . (($this->htmlAreaRTE->is_FE() && $GLOBALS['TSFE']->absRefPrefix) ? $GLOBALS['TSFE']->absRefPrefix : '') . $this->htmlAreaRTE->writeTemporaryFile('', $button . '_' . $this->htmlAreaRTE->contentLanguageUid, 'js', $languagesJSArray) . '";';
                return $registerRTEinJavascriptString;
        }
        /**
index e4bbb5a..653e0f7 100644 (file)
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2008-2011 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  (c) 2008-2012 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the Typo3 project. The Typo3 project is
@@ -192,7 +192,7 @@ class tx_rtehtmlarea_selectfont extends tx_rtehtmlarea_api {
                        RTEarea['.$RTEcounter.'].buttons.'. $buttonId .' = new Object();';
                }
                $configureRTEInJavascriptString .= '
-                       RTEarea['.$RTEcounter.'].buttons.'. $buttonId . '.dataUrl = \'' . $this->htmlAreaRTE->writeTemporaryFile('', $buttonId . '_'. $this->htmlAreaRTE->contentLanguageUid, 'js', $itemsJSArray) . '\';';
+                       RTEarea['.$RTEcounter.'].buttons.'. $buttonId . '.dataUrl = "' . (($this->htmlAreaRTE->is_FE() && $GLOBALS['TSFE']->absRefPrefix) ? $GLOBALS['TSFE']->absRefPrefix : '') . $this->htmlAreaRTE->writeTemporaryFile('', $buttonId . '_'. $this->htmlAreaRTE->contentLanguageUid, 'js', $itemsJSArray) . '";';
                return $configureRTEInJavascriptString;
        }
 }
diff --git a/typo3/sysext/rtehtmlarea/htmlarea/htmlarea-gecko.js b/typo3/sysext/rtehtmlarea/htmlarea/htmlarea-gecko.js
deleted file mode 100644 (file)
index 5913c58..0000000
+++ /dev/null
@@ -1,869 +0,0 @@
-/***************************************************************
-*  Copyright notice
-*
-*  (c) 2002-2004 interactivetools.com, inc.
-*  (c) 2003-2004 dynarch.com
-*  (c) 2004-2010 Stanislas Rolland <typo3(arobas)sjbr.ca>
-*  All rights reserved
-*
-*  This script is part of the TYPO3 project. The TYPO3 project is
-*  free software; you can redistribute it and/or modify
-*  it under the terms of the GNU General Public License as published by
-*  the Free Software Foundation; either version 2 of the License, or
-*  (at your option) any later version.
-*
-*  The GNU General Public License can be found at
-*  http://www.gnu.org/copyleft/gpl.html.
-*  A copy is found in the textfile GPL.txt and important notices to the license
-*  from the author is found in LICENSE.txt distributed with these scripts.
-*
-*
-*  This script is distributed in the hope that it will be useful,
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-*  GNU General Public License for more details.
-*
-*  This script is a modified version of a script published under the htmlArea License.
-*  A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
-*
-*  This copyright notice MUST APPEAR in all copies of the script!
-***************************************************************/
-/*
- */
-
-/***************************************************
- *  GECKO-SPECIFIC FUNCTIONS
- ***************************************************/
-HTMLArea.Editor.prototype.isEditable = function() {
-       return (this._doc.designMode === "on");
-};
-/***************************************************
- *  SELECTIONS AND RANGES
- ***************************************************/
-
-/*
- * Get the current selection object
- */
-HTMLArea.Editor.prototype._getSelection = function() {
-       return this._iframe.contentWindow.getSelection();
-};
-
-/*
- * Empty the selection object
- */
-HTMLArea.Editor.prototype.emptySelection = function(selection) {
-       if (Ext.isWebKit) {
-               if (Ext.isFunction(selection.removeAllRanges)) {
-                       selection.removeAllRanges();
-               } else {
-                       selection.empty();
-               }
-       } else {
-               selection.removeAllRanges();
-       }
-       if (Ext.isOpera) {
-               this.focus();
-       }
-};
-
-/*
- * Add a range to the selection
- */
-HTMLArea.Editor.prototype.addRangeToSelection = function(selection, range) {
-       if (Ext.isWebKit) {
-               if (Ext.isFunction(selection.addRange)) {
-                       selection.addRange(range);
-               } else {
-                       selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
-               }
-       } else {
-               selection.addRange(range);
-       }
-};
-
-/*
- * Create a range for the current selection
- */
-HTMLArea.Editor.prototype._createRange = function(sel) {
-       if (Ext.isEmpty(sel)) {
-               return this._doc.createRange();
-       }
-               // Older versions of WebKit did not support getRangeAt
-       if (Ext.isWebKit && !sel.getRangeAt) {
-               var range = this._doc.createRange();
-               if (typeof(sel) == "undefined") {
-                       return range;
-               } else if (sel.baseNode == null) {
-                       range.setStart(this._doc.body,0);
-                       range.setEnd(this._doc.body,0);
-                       return range;
-               } else {
-                       range.setStart(sel.baseNode, sel.baseOffset);
-                       range.setEnd(sel.extentNode, sel.extentOffset);
-                       if (range.collapsed != sel.isCollapsed) {
-                               range.setStart(sel.extentNode, sel.extentOffset);
-                               range.setEnd(sel.baseNode, sel.baseOffset);
-                       }
-                       return range;
-               }
-       }
-       try {
-               return sel.getRangeAt(0);
-       } catch(e) {
-               return this._doc.createRange();
-       }
-};
-
-/*
- * Select a node AND the contents inside the node
- */
-HTMLArea.Editor.prototype.selectNode = function(node, endPoint) {
-       this.focus();
-       var selection = this._getSelection();
-       if (Ext.isWebKit && /^(img)$/i.test(node.nodeName)) {
-               this._getSelection().setBaseAndExtent(node, 0, node, 1);
-       } else {
-               var range = this._doc.createRange();
-               if (node.nodeType == 1 && node.nodeName.toLowerCase() == "body") {
-                       if (Ext.isWebKit) {
-                               range.setStart(node, 0);
-                               range.setEnd(node, node.childNodes.length);
-                       } else {
-                               range.selectNodeContents(node);
-                       }
-               } else {
-                       range.selectNode(node);
-               }
-               if (typeof(endPoint) != "undefined") {
-                       range.collapse(endPoint);
-               }
-               this.emptySelection(selection);
-               this.addRangeToSelection(selection, range);
-       }
-};
-/*
- * Select ONLY the contents inside the given node
- */
-HTMLArea.Editor.prototype.selectNodeContents = function(node, endPoint) {
-       this.focus();
-       var selection = this._getSelection();
-       var range = this._doc.createRange();
-       if (Ext.isWebKit) {
-               range.setStart(node, 0);
-               if (node.nodeType == 3 || node.nodeType == 8 || node.nodeType == 4) {
-                       range.setEnd(node, node.textContent.length);
-               } else {
-                       range.setEnd(node, node.childNodes.length);
-               }
-       } else {
-               range.selectNodeContents(node);
-       }
-       if (typeof(endPoint) !== "undefined") {
-               range.collapse(endPoint);
-       }
-       this.emptySelection(selection);
-       this.addRangeToSelection(selection, range);
-};
-HTMLArea.Editor.prototype.rangeIntersectsNode = function(range, node) {
-       var nodeRange = this._doc.createRange();
-       try {
-               nodeRange.selectNode(node);
-       } catch (e) {
-               if (Ext.isWebKit) {
-                       nodeRange.setStart(node, 0);
-                       if (node.nodeType == 3 || node.nodeType == 8 || node.nodeType == 4) {
-                               nodeRange.setEnd(node, node.textContent.length);
-                       } else {
-                               nodeRange.setEnd(node, node.childNodes.length);
-                       }
-               } else {
-                       nodeRange.selectNodeContents(node);
-               }
-       }
-               // Note: sometimes WebKit inverts the end points
-       return (range.compareBoundaryPoints(range.END_TO_START, nodeRange) == -1 && range.compareBoundaryPoints(range.START_TO_END, nodeRange) == 1) ||
-               (range.compareBoundaryPoints(range.END_TO_START, nodeRange) == 1 && range.compareBoundaryPoints(range.START_TO_END, nodeRange) == -1);
-};
-/*
- * Get the selection type
- */
-HTMLArea.Editor.prototype.getSelectionType = function(selection) {
-               // By default set the type to "Text".
-       var type = 'Text';
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-                       // Check if the actual selection is a Control
-       if (selection && selection.rangeCount == 1) {
-               var range = selection.getRangeAt(0);
-               if (range.startContainer.nodeType == 1) {
-                       if (
-                                       // Gecko
-                               (range.startContainer == range.endContainer && (range.endOffset - range.startOffset) == 1) ||
-                                       // Opera and WebKit
-                               (range.endContainer.nodeType == 3 && range.endOffset == 0 && range.startContainer.childNodes[range.startOffset].nextSibling == range.endContainer)
-                       ) {
-                               if (/^(img|hr|li|table|tr|td|embed|object|ol|ul|dl)$/i.test(range.startContainer.childNodes[range.startOffset].nodeName)) {
-                                       type = 'Control';
-                               }
-                       }
-               }
-       }
-       return type;
-};
-/*
- * Return the ranges of the selection
- */
-HTMLArea.Editor.prototype.getSelectionRanges = function(selection) {
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       var ranges = [];
-               // Older versions of WebKit did not support getRangeAt
-       if (selection.getRangeAt) {
-               for (var i = selection.rangeCount; --i >= 0;) {
-                       ranges.push(selection.getRangeAt(i));
-               }
-       }
-       return ranges;
-};
-/*
- * Add ranges to the selection
- */
-HTMLArea.Editor.prototype.setSelectionRanges = function(ranges, selection) {
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       if (selection.getRangeAt) {
-               this.emptySelection(selection);
-               for (var i = ranges.length; --i >= 0;) {
-                       this.addRangeToSelection(selection, ranges[i]);
-               }
-       }
-};
-/*
- * Retrieves the selected element (if any), just in the case that a single element (object like and image or a table) is selected.
- */
-HTMLArea.Editor.prototype.getSelectedElement = function(selection) {
-       var selectedElement = null;
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       if (selection && selection.anchorNode && selection.anchorNode.nodeType == 1) {
-               if (this.getSelectionType(selection) == "Control") {
-                       selectedElement = selection.anchorNode.childNodes[selection.anchorOffset];
-                               // For Safari, the anchor node for a control selection is the control itself
-                       if (!selectedElement) {
-                               selectedElement = selection.anchorNode;
-                       } else if (selectedElement.nodeType != 1) {
-                               return null;
-                       }
-               }
-       }
-       return selectedElement;
-};
-
-/*
- * Retrieve the HTML contents of selected block
- */
-HTMLArea.Editor.prototype.getSelectedHTML = function() {
-       var range = this._createRange(this._getSelection());
-       if (range.collapsed) return "";
-       var cloneContents = range.cloneContents();
-       if (!cloneContents) {
-               cloneContents = this._doc.createDocumentFragment();
-       }
-       return HTMLArea.getHTML(cloneContents, false, this);
-};
-
-/*
- * Retrieve simply HTML contents of the selected block, IE ignoring control ranges
- */
-HTMLArea.Editor.prototype.getSelectedHTMLContents = function() {
-       return this.getSelectedHTML();
-};
-
-/*
- * Get the deepest node that contains both endpoints of the current selection.
- */
-HTMLArea.Editor.prototype.getParentElement = function(selection, range) {
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       if (this.getSelectionType(selection) === "Control") {
-               return this.getSelectedElement(selection);
-       }
-       if (typeof(range) === "undefined") {
-               var range = this._createRange(selection);
-       }
-       var parentElement = range.commonAncestorContainer;
-               // For some reason, Firefox 3 may report the Document as commonAncestorContainer
-       if (parentElement.nodeType == 9) return this._doc.body;
-       while (parentElement && parentElement.nodeType == 3) {
-               parentElement = parentElement.parentNode;
-       }
-       return parentElement;
-};
-
-/*
- * Get the selected element, if any.  That is, the element that you have last selected in the "path"
- * at the bottom of the editor, or a "control" (eg image)
- *
- * @returns null | element
- * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
- */
-HTMLArea.Editor.prototype._activeElement = function(selection) {
-       if (this._selectionEmpty(selection)) {
-               return null;
-       }
-               // Check if the anchor (start of selection) is an element.
-       if (selection.anchorNode.nodeType == 1) {
-               return selection.anchorNode;
-       } else {
-               return null;
-       }
-};
-
-/*
- * Determine if the current selection is empty or not.
- */
-HTMLArea.Editor.prototype._selectionEmpty = function(sel) {
-       if (!sel) return true;
-       return sel.isCollapsed;
-};
-
-/*
- * Get a bookmark
- * Adapted from FCKeditor
- * This is an "intrusive" way to create a bookmark. It includes <span> tags
- * in the range boundaries. The advantage of it is that it is possible to
- * handle DOM mutations when moving back to the bookmark.
- */
-HTMLArea.Editor.prototype.getBookmark = function (range) {
-               // Create the bookmark info (random IDs).
-       var bookmark = {
-               startId : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'S',
-               endId   : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'E'
-       };
-
-       var startSpan;
-       var endSpan;
-       var rangeClone = range.cloneRange();
-
-               // For collapsed ranges, add just the start marker.
-       if (!range.collapsed ) {
-               endSpan = this._doc.createElement("span");
-               endSpan.style.display = "none";
-               endSpan.id = bookmark.endId;
-               endSpan.setAttribute("HTMLArea_bookmark", true);
-               endSpan.innerHTML = "&nbsp;";
-               rangeClone.collapse(false);
-               rangeClone.insertNode(endSpan);
-       }
-
-       startSpan = this._doc.createElement("span");
-       startSpan.style.display = "none";
-       startSpan.id = bookmark.startId;
-       startSpan.setAttribute("HTMLArea_bookmark", true);
-       startSpan.innerHTML = "&nbsp;";
-       var rangeClone = range.cloneRange();
-       rangeClone.collapse(true);
-       rangeClone.insertNode(startSpan);
-       bookmark.startNode = startSpan;
-       bookmark.endNode = endSpan;
-               // Update the range position.
-       if (endSpan) {
-               range.setEndBefore(endSpan);
-               range.setStartAfter(startSpan);
-       } else {
-               range.setEndAfter(startSpan);
-               range.collapse(false);
-       }
-       return bookmark;
-};
-
-/*
- * Get the end point of the bookmark
- * Adapted from FCKeditor
- */
-HTMLArea.Editor.prototype.getBookmarkNode = function(bookmark, endPoint) {
-       if (endPoint) {
-               return this._doc.getElementById(bookmark.startId);
-       } else {
-               return this._doc.getElementById(bookmark.endId);
-       }
-};
-
-/*
- * Move the range to the bookmark
- * Adapted from FCKeditor
- */
-HTMLArea.Editor.prototype.moveToBookmark = function (bookmark) {
-       var startSpan  = this.getBookmarkNode(bookmark, true);
-       var endSpan    = this.getBookmarkNode(bookmark, false);
-       var parent;
-       var range = this._createRange();
-       if (startSpan) {
-                       // If the previous sibling is a text node, let the anchorNode have it as parent
-               if (startSpan.previousSibling && startSpan.previousSibling.nodeType == 3) {
-                       range.setStart(startSpan.previousSibling, startSpan.previousSibling.data.length);
-               } else {
-                       range.setStartBefore(startSpan);
-               }
-               HTMLArea.removeFromParent(startSpan);
-       } else {
-                       // For some reason, the startSpan was removed or its id attribute was removed so that it cannot be retrieved
-               range.setStart(this._doc.body, 0);
-       }
-               // If the bookmarked range was collapsed, the end span will not be available
-       if (endSpan) {
-                       // If the next sibling is a text node, let the focusNode have it as parent
-               if (endSpan.nextSibling && endSpan.nextSibling.nodeType == 3) {
-                       range.setEnd(endSpan.nextSibling, 0);
-               } else {
-                       range.setEndBefore(endSpan);
-               }
-               HTMLArea.removeFromParent(endSpan);
-       } else {
-               range.collapse(true);
-       }
-       return range;
-};
-
-/*
- * Select range
- */
-HTMLArea.Editor.prototype.selectRange = function (range) {
-       var selection = this._getSelection();
-       this.emptySelection(selection);
-       this.addRangeToSelection(selection, range);
-};
-
-/***************************************************
- *  DOM TREE MANIPULATION
- ***************************************************/
-
- /*
- * Insert a node at the current position.
- * Delete the current selection, if any.
- * Split the text node, if needed.
- */
-HTMLArea.Editor.prototype.insertNodeAtSelection = function(toBeInserted) {
-       this.focus();
-       var range = this._createRange(this._getSelection());
-       range.deleteContents();
-       var toBeSelected = (toBeInserted.nodeType === 11) ? toBeInserted.lastChild : toBeInserted;
-       range.insertNode(toBeInserted);
-       this.selectNodeContents(toBeSelected, false);
-};
-
-/*
- * Insert HTML source code at the current position.
- * Delete the current selection, if any.
- */
-HTMLArea.Editor.prototype.insertHTML = function(html) {
-       this.focus();
-       var fragment = this._doc.createDocumentFragment();
-       var div = this._doc.createElement("div");
-       div.innerHTML = html;
-       while (div.firstChild) {
-               fragment.appendChild(div.firstChild);
-       }
-       this.insertNodeAtSelection(fragment);
-};
-
-/*
- * Wrap the range with an inline element
- *
- * @param      string  element: the node that will wrap the range
- * @param      object  selection: the selection object
- * @param      object  range: the range to be wrapped
- *
- * @return     void
- */
-HTMLArea.Editor.prototype.wrapWithInlineElement = function(element, selection, range) {
-       element.appendChild(range.extractContents());
-       range.insertNode(element);
-       element.normalize();
-               // Sometimes Firefox inserts empty elements just outside the boundaries of the range
-       var neighbour = element.previousSibling;
-       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-               HTMLArea.removeFromParent(neighbour);
-       }
-       neighbour = element.nextSibling;
-       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-               HTMLArea.removeFromParent(neighbour);
-       }
-       this.selectNodeContents(element, false);
-};
-
-/*
- * Clean Apple wrapping span and font tags under the specified node
- *
- * @param      object  node: the node in the subtree of which cleaning is performed
- *
- * @return     void
- */
-HTMLArea.Editor.prototype.cleanAppleStyleSpans = function(node) {
-       if (Ext.isWebKit) {
-               if (node.getElementsByClassName) {
-                       var spans = node.getElementsByClassName("Apple-style-span");
-                       for (var i = spans.length; --i >= 0;) {
-                               this.removeMarkup(spans[i]);
-                       }
-               } else {
-                       var spans = node.getElementsByTagName("span");
-                       for (var i = spans.length; --i >= 0;) {
-                               if (HTMLArea.DOM.hasClass(spans[i], "Apple-style-span")) {
-                                       this.removeMarkup(spans[i]);
-                               }
-                       }
-                       var fonts = node.getElementsByTagName("font");
-                       for (i = fonts.length; --i >= 0;) {
-                               if (HTMLArea.DOM.hasClass(fonts[i], "Apple-style-span")) {
-                                       this.removeMarkup(fonts[i]);
-                               }
-                       }
-               }
-       }
-};
-
-/***************************************************
- *  EVENTS HANDLERS
- ***************************************************/
-/*
- * Backspace event handler
- */
-HTMLArea.Editor.prototype._checkBackspace = function() {
-       var self = this;
-       window.setTimeout(function() {
-               var selection = self._getSelection();
-               var range = self._createRange(selection);
-               var startContainer = range.startContainer;
-               var startOffset = range.startOffset;
-                       // If the selection is collapsed...
-               if (self._selectionEmpty()) {
-                               // ... and the cursor lies in a direct child of body...
-                       if (/^(body)$/i.test(startContainer.nodeName)) {
-                               var node = startContainer.childNodes[startOffset];
-                       } else if (/^(body)$/i.test(startContainer.parentNode.nodeName)) {
-                               var node = startContainer;
-                       } else {
-                               return false;
-                       }
-                               // ... which is a br or text node containing no non-whitespace character
-                       if (/^(br|#text)$/i.test(node.nodeName) && !/\S/.test(node.textContent)) {
-                                       // Get a meaningful previous sibling in which to reposition de cursor
-                               var previousSibling = node.previousSibling;
-                               while (previousSibling && /^(br|#text)$/i.test(previousSibling.nodeName) && !/\S/.test(previousSibling.textContent)) {
-                                       previousSibling = previousSibling.previousSibling;
-                               }
-                                       // If there is no meaningful previous sibling, the cursor is at the start of body
-                               if (previousSibling) {
-                                               // Remove the node
-                                       HTMLArea.removeFromParent(node);
-                                               // Position the cursor
-                                       if (/^(ol|ul|dl)$/i.test(previousSibling.nodeName)) {
-                                               self.selectNodeContents(previousSibling.lastChild, false);
-                                       } else if (/^(table)$/i.test(previousSibling.nodeName)) {
-                                               self.selectNodeContents(previousSibling.rows[previousSibling.rows.length-1].cells[previousSibling.rows[previousSibling.rows.length-1].cells.length-1], false);
-                                       } else if (!/\S/.test(previousSibling.textContent) && previousSibling.firstChild) {
-                                               self.selectNode(previousSibling.firstChild, true);
-                                       } else {
-                                               self.selectNodeContents(previousSibling, false);
-                                       }
-                               }
-                       }
-               }
-       }, 10);
-       return false;
-};
-
-/*
- * Enter event handler
- */
-HTMLArea.Editor.prototype._checkInsertP = function() {
-       var editor = this;
-       this.focus();
-       var i, left, right, rangeClone,
-               sel     = this._getSelection(),
-               range   = this._createRange(sel),
-               p       = this.getAllAncestors(),
-               block   = null,
-               a       = null,
-               doc     = this._doc;
-       for (i = 0; i < p.length; ++i) {
-               if (HTMLArea.isBlockElement(p[i]) && !/^(html|body|table|tbody|thead|tfoot|tr|dl)$/i.test(p[i].nodeName)) {
-                       block = p[i];
-                       break;
-               }
-       }
-       if (block && /^(td|th|tr|tbody|thead|tfoot|table)$/i.test(block.nodeName) && this.config.buttons.table && this.config.buttons.table.disableEnterParagraphs) return false;
-       if (!range.collapsed) {
-               range.deleteContents();
-       }
-       this.emptySelection(sel);
-       if (!block || /^(td|div)$/i.test(block.nodeName)) {
-               if (!block) {
-                       block = doc.body;
-               }
-               if (block.hasChildNodes()) {
-                       rangeClone = range.cloneRange();
-                       if (range.startContainer == block) {
-                                       // Selection is directly under the block
-                               var blockOnLeft = null;
-                               var leftSibling = null;
-                                       // Looking for the farthest node on the left that is not a block
-                               for (var i = range.startOffset; --i >= 0;) {
-                                       if (HTMLArea.isBlockElement(block.childNodes[i])) {
-                                               blockOnLeft = block.childNodes[i];
-                                               break;
-                                       } else {
-                                               rangeClone.setStartBefore(block.childNodes[i]);
-                                       }
-                               }
-                       } else {
-                                       // Looking for inline or text container immediate child of block
-                               var inlineContainer = range.startContainer;
-                               while (inlineContainer.parentNode != block) {
-                                       inlineContainer = inlineContainer.parentNode;
-                               }
-                                       // Looking for the farthest node on the left that is not a block
-                               var leftSibling = inlineContainer;
-                               while (leftSibling.previousSibling && !HTMLArea.isBlockElement(leftSibling.previousSibling)) {
-                                       leftSibling = leftSibling.previousSibling;
-                               }
-                               rangeClone.setStartBefore(leftSibling);
-                               var blockOnLeft = leftSibling.previousSibling;
-                       }
-                               // Avoiding surroundContents buggy in Opera and Safari
-                       left = doc.createElement('p');
-                       left.appendChild(rangeClone.extractContents());
-                       if (!left.textContent && !left.getElementsByTagName('img').length && !left.getElementsByTagName('table').length) {
-                               left.innerHTML = '<br />';
-                       }
-                       if (block.hasChildNodes()) {
-                               if (blockOnLeft) {
-                                       left = block.insertBefore(left, blockOnLeft.nextSibling);
-                               } else {
-                                       left = block.insertBefore(left, block.firstChild);
-                               }
-                       } else {
-                               left = block.appendChild(left);
-                       }
-                       block.normalize();
-                               // Looking for the farthest node on the right that is not a block
-                       var rightSibling = left;
-                       while (rightSibling.nextSibling && !HTMLArea.isBlockElement(rightSibling.nextSibling)) {
-                               rightSibling = rightSibling.nextSibling;
-                       }
-                       var blockOnRight = rightSibling.nextSibling;
-                       range.setEndAfter(rightSibling);
-                       range.setStartAfter(left);
-                               // Avoiding surroundContents buggy in Opera and Safari
-                       right = doc.createElement('p');
-                       right.appendChild(range.extractContents());
-                       if (!right.textContent && !right.getElementsByTagName('img').length && !right.getElementsByTagName('table').length) {
-                               right.innerHTML = '<br />';
-                       }
-                       if (!(left.childNodes.length == 1 && right.childNodes.length == 1 && left.firstChild.nodeName.toLowerCase() == 'br' && right.firstChild.nodeName.toLowerCase() == 'br')) {
-                               if (blockOnRight) {
-                                       right = block.insertBefore(right, blockOnRight);
-                               } else {
-                                       right = block.appendChild(right);
-                               }
-                               this.selectNodeContents(right, true);
-                       } else {
-                               this.selectNodeContents(left, true);
-                       }
-                       block.normalize();
-               } else {
-                       var first = block.firstChild;
-                       if (first) {
-                               block.removeChild(first);
-                       }
-                       right = doc.createElement("p");
-                       if (Ext.isWebKit || Ext.isOpera) {
-                               right.innerHTML = "<br />";
-                       }
-                       right = block.appendChild(right);
-                       this.selectNodeContents(right, true);
-               }
-       } else {
-               range.setEndAfter(block);
-               var df = range.extractContents(), left_empty = false;
-               if (!/\S/.test(block.innerHTML) || (!/\S/.test(block.textContent) && !/<(img|hr|table)/i.test(block.innerHTML))) {
-                       if (!Ext.isOpera) {
-                               block.innerHTML = "<br />";
-                       }
-                       left_empty = true;
-               }
-               p = df.firstChild;
-               if (p) {
-                       if (!/\S/.test(p.innerHTML) || (!/\S/.test(p.textContent) && !/<(img|hr|table)/i.test(p.innerHTML))) {
-                               if (/^h[1-6]$/i.test(p.nodeName)) {
-                                       p = this.convertNode(p, "p");
-                               }
-                               if (/^(dt|dd)$/i.test(p.nodeName)) {
-                                        p = this.convertNode(p, (p.nodeName.toLowerCase() === "dt") ? "dd" : "dt");
-                               }
-                               if (!Ext.isOpera) {
-                                       p.innerHTML = "<br />";
-                               }
-                               if (/^li$/i.test(p.nodeName) && left_empty && (!block.nextSibling || !/^li$/i.test(block.nextSibling.nodeName))) {
-                                       left = block.parentNode;
-                                       left.removeChild(block);
-                                       range.setEndAfter(left);
-                                       range.collapse(false);
-                                       p = this.convertNode(p, /^(li|dd|td|th|p|h[1-6])$/i.test(left.parentNode.nodeName) ? "br" : "p");
-                               }
-                       }
-                       range.insertNode(df);
-                               // Remove any anchor created empty on both sides of the selection
-                       if (p.previousSibling) {
-                               var a = p.previousSibling.lastChild;
-                               if (a && /^a$/i.test(a.nodeName) && !/\S/.test(a.innerHTML)) {
-                                       this.convertNode(a, 'br');
-                               }
-                       }
-                       var a = p.lastChild;
-                       if (a && /^a$/i.test(a.nodeName) && !/\S/.test(a.innerHTML)) {
-                               this.convertNode(a, 'br');
-                       }
-                               // Walk inside the deepest child element (presumably inline element)
-                       while (p.firstChild && p.firstChild.nodeType == 1 && !/^(br|img|hr|table)$/i.test(p.firstChild.nodeName)) {
-                               p = p.firstChild;
-                       }
-                       if (/^br$/i.test(p.nodeName)) {
-                               p = p.parentNode.insertBefore(doc.createTextNode('\x20'), p);
-                       } else if (!/\S/.test(p.innerHTML)) {
-                                       // Need some element inside the deepest element
-                               p.appendChild(doc.createElement('br'));
-                       }
-                       this.selectNodeContents(p, true);
-               } else {
-                       if (/^(li|dt|dd)$/i.test(block.nodeName)) {
-                               p = doc.createElement(block.nodeName);
-                       } else {
-                               p = doc.createElement("p");
-                       }
-                       if (!Ext.isOpera) {
-                               p.innerHTML = "<br />";
-                       }
-                       if (block.nextSibling) {
-                               p = block.parentNode.insertBefore(p, block.nextSibling);
-                       } else {
-                               p = block.parentNode.appendChild(p);
-                       }
-                       this.selectNodeContents(p, true);
-               }
-       }
-       this.scrollToCaret();
-       return true;
-};
-
-/*
- * Detect emails and urls as they are typed in Mozilla
- * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
- */
-HTMLArea.Editor.prototype._detectURL = function(event) {
-       var ev = event.browserEvent;
-       var editor = this;
-       var s = this._getSelection();
-       if (this.getParentElement(s).nodeName.toLowerCase() != 'a') {
-               var autoWrap = function (textNode, tag) {
-                       var rightText = textNode.nextSibling;
-                       if (typeof(tag) == 'string') tag = editor._doc.createElement(tag);
-                       var a = textNode.parentNode.insertBefore(tag, rightText);
-                       HTMLArea.removeFromParent(textNode);
-                       a.appendChild(textNode);
-                       s.collapse(rightText, 0);
-                       rightText.parentNode.normalize();
-       
-                       editor._unLink = function() {
-                               var t = a.firstChild;
-                               a.removeChild(t);
-                               a.parentNode.insertBefore(t, a);
-                               HTMLArea.removeFromParent(a);
-                               t.parentNode.normalize();
-                               editor._unLink = null;
-                               editor._unlinkOnUndo = false;
-                       };
-       
-                       editor._unlinkOnUndo = true;
-                       return a;
-               };
-       
-               switch(ev.which) {
-                               // Space or Enter or >, see if the text just typed looks like a URL, or email address and link it accordingly
-                       case 13:
-                       case 32:
-                               if(s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0) {
-                                       var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/[a-zA-Z0-9]+\S{3,}$/);
-                                       if(midStart == -1) break;
-                                       if(this._getFirstAncestor(s, 'a')) break; // already in an anchor
-                                       var matchData = s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/, '$1');
-                                       if (matchData.indexOf('@') != -1) {
-                                               var m = matchData.match(HTMLArea.RE_email);
-                                               if(m) {
-                                                       var leftText  = s.anchorNode;
-                                                       var rightText = leftText.splitText(s.anchorOffset);
-                                                       var midText   = leftText.splitText(midStart);
-                                                       var midEnd = midText.data.search(/[^a-zA-Z0-9\.@_\-]/);
-                                                       if (midEnd != -1) var endText = midText.splitText(midEnd);
-                                                       autoWrap(midText, 'a').href = 'mailto:' + m[0];
-                                                       break;
-                                               }
-                                       }
-                                       var m = matchData.match(HTMLArea.RE_url);
-                                       if(m) {
-                                               var leftText  = s.anchorNode;
-                                               var rightText = leftText.splitText(s.anchorOffset);
-                                               var midText   = leftText.splitText(midStart);
-                                               var midEnd = midText.data.search(/[^a-zA-Z0-9\._\-\/\&\?=:@]/);
-                                               if (midEnd != -1) var endText = midText.splitText(midEnd);
-                                               autoWrap(midText, 'a').href = (m[1] ? m[1] : 'http://') + m[3];
-                                               break;
-                                       }
-                               }
-                               break;
-                       default:
-                               if(ev.keyCode == 27 || (editor._unlinkOnUndo && ev.ctrlKey && ev.which == 122) ) {
-                                       if(this._unLink) {
-                                               this._unLink();
-                                               event.stopEvent();
-                                       }
-                                       break;
-                               } else if(ev.which || ev.keyCode == 8 || ev.keyCode == 46) {
-                                       this._unlinkOnUndo = false;
-                                       if(s.anchorNode && s.anchorNode.nodeType == 3) {
-                                                       // See if we might be changing a link
-                                               var a = this._getFirstAncestor(s, 'a');
-                                               if(!a) break; // not an anchor
-                                               if(!a._updateAnchTimeout) {
-                                                       if(s.anchorNode.data.match(HTMLArea.RE_email) && (a.href.match('mailto:' + s.anchorNode.data.trim()))) {
-                                                               var textNode = s.anchorNode;
-                                                               var fn = function() {
-                                                                       a.href = 'mailto:' + textNode.data.trim();
-                                                                       a._updateAnchTimeout = setTimeout(fn, 250);
-                                                               };
-                                                               a._updateAnchTimeout = setTimeout(fn, 250);
-                                                               break;
-                                                       }
-                                                       var m = s.anchorNode.data.match(HTMLArea.RE_url);
-                                                       if(m &&  a.href.match(s.anchorNode.data.trim())) {
-                                                               var textNode = s.anchorNode;
-                                                               var fn = function() {
-                                                                       var m = textNode.data.match(HTMLArea.RE_url);
-                                                                       a.href = (m[1] ? m[1] : 'http://') + m[3];
-                                                                       a._updateAnchTimeout = setTimeout(fn, 250);
-                                                               }
-                                                               a._updateAnchTimeout = setTimeout(fn, 250);
-                                                       }
-                                               }
-                                       }
-                               }
-                               break;
-               }
-       }
-};
diff --git a/typo3/sysext/rtehtmlarea/htmlarea/htmlarea-ie.js b/typo3/sysext/rtehtmlarea/htmlarea/htmlarea-ie.js
deleted file mode 100644 (file)
index 652d986..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-/***************************************************************
-*  Copyright notice
-*
-*  (c) 2002-2004 interactivetools.com, inc.
-*  (c) 2003-2004 dynarch.com
-*  (c) 2004-2010 Stanislas Rolland <typo3(arobas)sjbr.ca>
-*  All rights reserved
-*
-*  This script is part of the TYPO3 project. The TYPO3 project is
-*  free software; you can redistribute it and/or modify
-*  it under the terms of the GNU General Public License as published by
-*  the Free Software Foundation; either version 2 of the License, or
-*  (at your option) any later version.
-*
-*  The GNU General Public License can be found at
-*  http://www.gnu.org/copyleft/gpl.html.
-*  A copy is found in the textfile GPL.txt and important notices to the license
-*  from the author is found in LICENSE.txt distributed with these scripts.
-*
-*
-*  This script is distributed in the hope that it will be useful,
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-*  GNU General Public License for more details.
-*
-*  This script is a modified version of a script published under the htmlArea License.
-*  A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
-*
-*  This copyright notice MUST APPEAR in all copies of the script!
-***************************************************************/
-/*
- */
-
-/***************************************************
- *  IE-SPECIFIC FUNCTIONS
- ***************************************************/
-HTMLArea.Editor.prototype.isEditable = function() {
-       return this._doc.body.contentEditable;
-};
-
-/***************************************************
- *  SELECTIONS AND RANGES
- ***************************************************/
-/*
- * Get the current selection object
- */
-HTMLArea.Editor.prototype._getSelection = function() {
-       return this._doc.selection;
-};
-
-/*
- * Create a range for the current selection
- */
-HTMLArea.Editor.prototype._createRange = function(sel) {
-       if (typeof(sel) == "undefined") {
-               var sel = this._getSelection();
-       }
-       if (sel.type.toLowerCase() == "none") {
-               this.focus();
-       }
-       return sel.createRange();
-};
-
-/*
- * Select a node AND the contents inside the node
- */
-HTMLArea.Editor.prototype.selectNode = function(node) {
-       this.focus();
-       this.forceRedraw();
-       var range = this._doc.body.createTextRange();
-       range.moveToElementText(node);
-       range.select();
-};
-
-/*
- * Select ONLY the contents inside the given node
- */
-HTMLArea.Editor.prototype.selectNodeContents = function(node, endPoint) {
-       this.focus();
-       this.forceRedraw();
-       var range = this._doc.body.createTextRange();
-       range.moveToElementText(node);
-       if (typeof(endPoint) !== "undefined") {
-               range.collapse(endPoint);
-       }
-       range.select();
-};
-
-/*
- * Determine whether the node intersects the range
- */
-HTMLArea.Editor.prototype.rangeIntersectsNode = function(range, node) {
-       this.focus();
-       var nodeRange = this._doc.body.createTextRange();
-       nodeRange.moveToElementText(node);
-       return (range.compareEndPoints("EndToStart", nodeRange) == -1 && range.compareEndPoints("StartToEnd", nodeRange) == 1) ||
-               (range.compareEndPoints("EndToStart", nodeRange) == 1 && range.compareEndPoints("StartToEnd", nodeRange) == -1);
-};
-
-/*
- * Retrieve the HTML contents of selected block
- */
-HTMLArea.Editor.prototype.getSelectedHTML = function() {
-       var sel = this._getSelection();
-       var range = this._createRange(sel);
-       if (sel.type.toLowerCase() == "control") {
-               var r1 = this._doc.body.createTextRange();
-               r1.moveToElementText(range(0));
-               return r1.htmlText;
-       } else {
-               return range.htmlText;
-       }
-};
-
-/*
- * Retrieve simply HTML contents of the selected block, IE ignoring control ranges
- */
-HTMLArea.Editor.prototype.getSelectedHTMLContents = function() {
-       var sel = this._getSelection();
-       var range = this._createRange(sel);
-       return range.htmlText;
-};
-
-/*
- * Get the deepest node that contains both endpoints of the current selection.
- */
-HTMLArea.Editor.prototype.getParentElement = function(selection, range) {
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       if (typeof(range) === "undefined") {
-               var range = this._createRange(selection);
-       }
-       switch (selection.type.toLowerCase()) {
-               case "text":
-               case "none":
-                       var el = range.parentElement();
-                       if (el.nodeName.toLowerCase() == 'form') {
-                               return this._doc.body;
-                       }
-                       if (el.nodeName.toLowerCase() == "li" && range.htmlText.replace(/\s/g,"") == el.parentNode.outerHTML.replace(/\s/g,"")) {
-                               return el.parentNode;
-                       }
-                       return el;
-               case "control": return range.item(0);
-               default: return this._doc.body;
-       }
-};
-
-/*
- * Get the selected element, if any.  That is, the element that you have last selected in the "path"
- * at the bottom of the editor, or a "control" (eg image)
- *
- * @returns null | element
- * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
- */
-HTMLArea.Editor.prototype._activeElement = function(sel) {
-       if (sel == null) {
-               return null;
-       }
-       if (this._selectionEmpty(sel)) {
-               return null;
-       }
-       if (sel.type.toLowerCase() == "control") {
-               return sel.createRange().item(0);
-       } else {
-                       // If it's not a control, then we need to see if the selection is the _entire_ text of a parent node
-                       // (this happens when a node is clicked in the tree)
-               var range = sel.createRange();
-               var p_elm = this.getParentElement(sel);
-               if(p_elm.outerHTML == range.htmlText) return p_elm;
-               return null;
-       }
-};
-
-/*
- * Determine if the current selection is empty or not.
- */
-HTMLArea.Editor.prototype._selectionEmpty = function(selection) {
-       if (!selection || selection.type.toLowerCase() === "none") return true;
-       if (selection.type.toLowerCase() === "text") {
-               return !this._createRange(selection).text;
-       }
-       return !this._createRange(selection).htmlText;
-};
-
-/*
- * Get a bookmark
- */
-HTMLArea.Editor.prototype.getBookmark = function (range) {
-               // Bookmarking will not work on control ranges
-       try {
-               return range.getBookmark();
-       } catch (e) {
-               return null;
-       }
-};
-
-/*
- * Move the range to the bookmark
- */
-HTMLArea.Editor.prototype.moveToBookmark = function (bookmark) {
-       var range = this._createRange();
-       if (bookmark) {
-               range.moveToBookmark(bookmark);
-       }
-       return range;
-};
-
-/*
- * Select range
- */
-HTMLArea.Editor.prototype.selectRange = function (range) {
-       range.select();
-};
-/***************************************************
- *  DOM TREE MANIPULATION
- ***************************************************/
-
- /*
- * Insert a node at the current position.
- * Delete the current selection, if any.
- * Split the text node, if needed.
- */
-HTMLArea.Editor.prototype.insertNodeAtSelection = function(toBeInserted) {
-       this.insertHTML(toBeInserted.outerHTML);
-};
-
-/*
- * Insert HTML source code at the current position.
- * Delete the current selection, if any.
- */
-HTMLArea.Editor.prototype.insertHTML = function(html) {
-       var sel = this._getSelection();
-       if (sel.type.toLowerCase() == "control") {
-               sel.clear();
-               sel = this._getSelection();
-       }
-       var range = this._createRange(sel);
-       range.pasteHTML(html);
-};
-
-/*
- * Wrap the range with an inline element
- *
- * @param      string  element: the node that will wrap the range
- * @param      object  selection: the selection object
- * @param      object  range: the range to be wrapped
- *
- * @return     void
- */
-HTMLArea.Editor.prototype.wrapWithInlineElement = function(element, selection, range) {
-       var nodeName = element.nodeName;
-       var parent = this.getParentElement(selection, range);
-       var bookmark = this.getBookmark(range);
-       if (selection.type !== "Control") {
-               var rangeStart = range.duplicate();
-               rangeStart.collapse(true);
-               var parentStart = rangeStart.parentElement();
-               var rangeEnd = range.duplicate();
-               rangeEnd.collapse(true);
-               var newRange = this._createRange();
-               
-               var parentEnd = rangeEnd.parentElement();
-               var upperParentStart = parentStart;
-               if (parentStart !== parent) {
-                       while (upperParentStart.parentNode !== parent) {
-                               upperParentStart = upperParentStart.parentNode;
-                       }
-               }
-               
-               element.innerHTML = range.htmlText;
-                       // IE eats spaces on the start boundary
-               if (range.htmlText.charAt(0) === "\x20") {
-                       element.innerHTML = "&nbsp;" + element.innerHTML;
-               }
-               var elementClone = element.cloneNode(true);
-               range.pasteHTML(element.outerHTML);
-                       // IE inserts the element as the last child of the start container
-               if (parentStart !== parent
-                               && parentStart.lastChild
-                               && parentStart.lastChild.nodeType === 1
-                               && parentStart.lastChild.nodeName.toLowerCase() === nodeName) {
-                       parent.insertBefore(elementClone, upperParentStart.nextSibling);
-                       parentStart.removeChild(parentStart.lastChild);
-                               // Sometimes an empty previous sibling was created
-                       if (elementClone.previousSibling
-                                       && elementClone.previousSibling.nodeType === 1
-                                       && !elementClone.previousSibling.innerText) {
-                               parent.removeChild(elementClone.previousSibling);
-                       }
-                               // The bookmark will not work anymore
-                       newRange.moveToElementText(elementClone);
-                       newRange.collapse(false);
-                       newRange.select();
-               } else {
-                               // Working around IE boookmark bug
-                       if (parentStart != parentEnd) {
-                               var newRange = this._createRange();
-                               if (newRange.moveToBookmark(bookmark)) {
-                                       newRange.collapse(false);
-                                       newRange.select();
-                               }
-                       } else {
-                               range.collapse(false);
-                       }
-               }
-                       // normalize() is not available in IE5.5
-               try {
-                       parent.normalize();
-               } catch(e) { }
-       } else {
-               element = parent.parentNode.insertBefore(element, parent);
-               element.appendChild(parent);
-               this.moveToBookmark(bookmark);
-       }
-};
-
-/***************************************************
- *  EVENT HANDLERS
- ***************************************************/
-
-/*
- * Handle the backspace event in IE browsers
- */
-HTMLArea.Editor.prototype._checkBackspace = function() {
-       var selection = this._getSelection();
-       var range = this._createRange(selection);
-       if (selection.type == "Control"){ // Deleting or backspacing on a control selection : delete the element
-               var el = this.getParentElement();
-               var p = el.parentNode;
-               p.removeChild(el);
-               return true;
-       } else if (this._selectionEmpty(selection)) { // Check if deleting an empty block with a table as next sibling
-               var el = this.getParentElement();
-               if (!el.innerHTML && HTMLArea.isBlockElement(el) && el.nextSibling && /^table$/i.test(el.nextSibling.nodeName)) {
-                       var previous = el.previousSibling;
-                       if (!previous) {
-                               this.selectNodeContents(el.nextSibling.rows[0].cells[0], true);
-                       } else if (/^table$/i.test(previous.nodeName)) {
-                               this.selectNodeContents(previous.rows[previous.rows.length-1].cells[previous.rows[previous.rows.length-1].cells.length-1], false);
-                       } else {
-                               range.moveStart("character", -1);
-                               range.collapse(true);
-                               range.select();
-                       }
-                       el.parentNode.removeChild(el);
-                       return true;
-               }
-       } else { // Backspacing into a link
-               var r2 = range.duplicate();
-               r2.moveStart("character", -1);
-               var a = r2.parentElement();
-               if (a != range.parentElement() && /^a$/i.test(a.nodeName)) {
-                       r2.collapse(true);
-                       r2.moveEnd("character", 1);
-                       r2.pasteHTML('');
-                       r2.select();
-                       return true;
-               }
-               return false;
-       }
-};
index ba7d34c..a098a04 100644 (file)
@@ -83,6 +83,41 @@ Ext.apply(HTMLArea, {
                }
        },
        /*
+        * Create an editor when HTMLArea is loaded and when Ext is ready
+        *
+        * @param       string          editorId: the id of the editor
+        *
+        * @return      boolean         false if successful
+        */
+       initEditor: function (editorId) {
+               if (document.getElementById('pleasewait' + editorId)) {
+                       if (HTMLArea.checkSupportedBrowser()) {
+                               document.getElementById('pleasewait' + editorId).style.display = 'block';
+                               document.getElementById('editorWrap' + editorId).style.visibility = 'hidden';
+                               if (!HTMLArea.isReady) {
+                                       HTMLArea.initEditor.defer(150, null, [editorId]);
+                               } else {
+                                               // Create an editor for the textarea
+                                       var editor = new HTMLArea.Editor(Ext.apply(new HTMLArea.Config(editorId), RTEarea[editorId]));
+                                       editor.generate();
+                                       return false;
+                               }
+                       } else {
+                               document.getElementById('pleasewait' + editorId).style.display = 'none';
+                               document.getElementById('editorWrap' + editorId).style.visibility = 'visible';
+                       }
+               }
+               return true;
+       },
+       /*
+        * Check if the client agent is supported
+        *
+        * @return      boolean         true if the client is supported
+        */
+       checkSupportedBrowser: function () {
+               return Ext.isGecko || Ext.isWebKit || Ext.isOpera || Ext.isIE;
+       },
+       /*
         * Write message to JavaScript console
         *
         * @param       string          editorId: the id of the editor issuing the message
@@ -522,9 +557,8 @@ Ext.ux.form.HTMLAreaCombo = Ext.extend(Ext.form.ComboBox, {
                        var editor = this.getEditor();
                                // In IE, reclaim lost focus on the editor iframe and restore the bookmarked selection
                        if (Ext.isIE) {
-                               editor.focus();
                                if (!Ext.isEmpty(this.savedRange)) {
-                                       editor.selectRange(this.savedRange);
+                                       editor.getSelection().selectRange(this.savedRange);
                                        this.savedRange = null;
                                }
                        }
@@ -532,8 +566,7 @@ Ext.ux.form.HTMLAreaCombo = Ext.extend(Ext.form.ComboBox, {
                        this.plugins[this.action](editor, combo, record, index);
                                // In IE, bookmark the updated selection as the editor will be loosing focus
                        if (Ext.isIE) {
-                               editor.focus();
-                               this.savedRange = editor._createRange(editor._getSelection());
+                               this.savedRange = editor.getSelection().createRange();
                                this.triggered = true;
                        }
                        if (Ext.isOpera) {
@@ -568,7 +601,7 @@ Ext.ux.form.HTMLAreaCombo = Ext.extend(Ext.form.ComboBox, {
        saveSelection: function (event) {
                var editor = this.getEditor();
                if (editor.document.hasFocus()) {
-                       this.savedRange = editor._createRange(editor._getSelection());
+                       this.savedRange = editor.getSelection().createRange();
                }
        },
        /*
@@ -576,7 +609,7 @@ Ext.ux.form.HTMLAreaCombo = Ext.extend(Ext.form.ComboBox, {
         */
        restoreSelection: function (event) {
                if (!Ext.isEmpty(this.savedRange) && this.triggered) {
-                       this.getEditor().selectRange(this.savedRange);
+                       this.getEditor().getSelection().selectRange(this.savedRange);
                        this.triggered = false;
                }
        },
@@ -758,13 +791,14 @@ HTMLArea.Toolbar = Ext.extend(Ext.Container, {
        update: function() {
                var editor = this.getEditor(),
                        mode = editor.getMode(),
+                       selection = editor.getSelection(),
                        selectionEmpty = true,
                        ancestors = null,
                        endPointsInSameBlock = true;
                if (editor.getMode() === 'wysiwyg') {
-                       selectionEmpty = editor._selectionEmpty(editor._getSelection());
-                       ancestors = editor.getAllAncestors();
-                       endPointsInSameBlock = editor.endPointsInSameBlock();
+                       selectionEmpty = selection.isEmpty();
+                       ancestors = selection.getAllAncestors();
+                       endPointsInSameBlock = selection.endPointsInSameBlock();
                }
                this.fireEvent('HTMLAreaEventToolbarUpdate', mode, selectionEmpty, ancestors, endPointsInSameBlock);
        },
@@ -1211,7 +1245,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                if (!event.altKey && !event.ctrlKey) {
                                // Detect URL in non-IE browsers
                        if (!Ext.isIE && (event.getKey() != Ext.EventObject.ENTER || (event.shiftKey && !Ext.isWebKit))) {
-                               this.getEditor()._detectURL(event);
+                               this.getEditor().getSelection().detectURL(event);
                        }
                                // Handle option+SPACE for Mac users
                        if (Ext.isMac && event.browserEvent.charCode == 160) {
@@ -1238,7 +1272,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
        onMouse: function (event, target) {
                        // In WebKit, select the image when it is clicked
                if (Ext.isWebKit && /^(img)$/i.test(target.nodeName) && event.browserEvent.type == 'click') {
-                       this.getEditor().selectNode(target);
+                       this.getEditor().getSelection().selectNode(target);
                }
                this.getToolbar().updateLater.delay(100);
                return true;
@@ -1301,7 +1335,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                        return false;
                }
                if ((!Ext.isIE && !event.shiftKey) || Ext.isIE) {
-                       if (this.getEditor()._checkBackspace()) {
+                       if (this.getEditor().getSelection().handleBackSpace()) {
                                event.stopEvent();
                        }
                }
@@ -1316,8 +1350,8 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                if (this.inhibitKeyboardInput(event)) {
                        return false;
                }
-               this.getEditor()._detectURL(event);
-               if (this.getEditor()._checkInsertP()) {
+               this.getEditor().getSelection().detectURL(event);
+               if (this.getEditor().getSelection().checkInsertParagraph()) {
                        event.stopEvent();
                }
                        // Update the toolbar state after some time
@@ -1333,7 +1367,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                }
                if (event.shiftKey || this.config.disableEnterParagraphs) {
                        var editor = this.getEditor();
-                       editor._detectURL(event);
+                       editor.getSelection().detectURL(event);
                        if (Ext.isSafari) {
                                var brNode = editor.document.createElement('br');
                                editor.insertNodeAtSelection(brNode);
@@ -1346,7 +1380,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                                        var secondBrNode = editor.document.createElement('br');
                                        secondBrNode = brNode.parentNode.appendChild(secondBrNode);
                                }
-                               editor.selectNode(brNode, false);
+                               editor.getSelection().selectNode(brNode, false);
                                event.stopEvent();
                        }
                }
@@ -1361,7 +1395,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                if (this.inhibitKeyboardInput(event)) {
                        return false;
                }
-               this.getEditor().insertHTML('&nbsp;');
+               this.getEditor().getSelection().insertHtml('&nbsp;');
                event.stopEvent();
                return false;
        },
@@ -1372,7 +1406,7 @@ HTMLArea.Iframe = Ext.extend(Ext.BoxComponent, {
                if (this.inhibitKeyboardInput(event)) {
                        return false;
                }
-               this.getEditor().insertHTML('&nbsp;');
+               this.getEditor().getSelection().insertHtml('&nbsp;');
                event.stopEvent();
                return false;
        },
@@ -1637,7 +1671,7 @@ HTMLArea.StatusBar = Ext.extend(Ext.Container, {
         *
         * @param       object  element: set the status bar selection to the given element
         */
-       setSelection: function(element) {
+       setSelection: function (element) {
                this.selected = element ? element : null;
        },
        /*
@@ -1648,9 +1682,9 @@ HTMLArea.StatusBar = Ext.extend(Ext.Container, {
                element.blur();
                if (!Ext.isIE) {
                        if (/^(img|table)$/i.test(element.ancestor.nodeName)) {
-                               editor.selectNode(element.ancestor);
+                               editor.getSelection().selectNode(element.ancestor);
                        } else {
-                               editor.selectNodeContents(element.ancestor);
+                               editor.getSelection().selectNodeContents(element.ancestor);
                        }
                } else {
                        if (/^(img|table)$/i.test(element.ancestor.nodeName)) {
@@ -1658,7 +1692,7 @@ HTMLArea.StatusBar = Ext.extend(Ext.Container, {
                                range.addElement(element.ancestor);
                                range.select();
                        } else {
-                               editor.selectNode(element.ancestor);
+                               editor.getSelection().selectNode(element.ancestor);
                        }
                }
                this.setSelection(element.ancestor);
@@ -2062,6 +2096,50 @@ HTMLArea.Editor = Ext.extend(Ext.util.Observable, {
         */
        mode: 'textmode',
        /*
+        * Determine whether the editor document is currently contentEditable
+        *
+        * @return      boolean         true, if the document is contentEditable        
+        */
+       isEditable: function () {
+               return Ext.isIE ? this.document.body.contentEditable : (this.document.designMode === 'on');
+       },
+       /*
+        * The selection object
+        */
+       selection: null,
+       getSelection: function () {
+               if (!this.selection) {
+                       this.selection = new HTMLArea.DOM.Selection({
+                               editor: this
+                       });
+               }
+               return this.selection;
+       },
+       /*
+        * The bookmark object
+        */
+       bookMark: null,
+       getBookMark: function () {
+               if (!this.bookMark) {
+                       this.bookMark = new HTMLArea.DOM.BookMark({
+                               editor: this
+                       });
+               }
+               return this.bookMark;
+       },
+       /*
+        * The DOM node object
+        */
+       domNode: null,
+       getDomNode: function () {
+               if (!this.domNode) {
+                       this.domNode = new HTMLArea.DOM.Node({
+                               editor: this
+                       });
+               }
+               return this.domNode;
+       },
+       /*
         * Create the htmlArea framework
         */
        generate: function () {
@@ -2153,6 +2231,12 @@ HTMLArea.Editor = Ext.extend(Ext.util.Observable, {
        onFrameworkReady: function () {
                        // Initialize editor mode
                this.setMode('wysiwyg');
+                       // Create the selection object
+               this.getSelection();
+                       // Create the bookmark object
+               this.getBookMark();
+                       // Create the DOM node object
+               this.getDomNode();
                        // Initiate events listening
                this.initEventsListening();
                        // Generate plugins
@@ -2327,6 +2411,12 @@ HTMLArea.Editor = Ext.extend(Ext.util.Observable, {
                delete this.plugins[pluginName];
        },
        /*
+        * Update the edito toolbar
+        */
+       updateToolbar: function (noStatus) {
+               this.toolbar.update(noStatus);
+       },
+       /*
         * Focus on the editor
         */
        focus: function () {
@@ -2340,6 +2430,21 @@ HTMLArea.Editor = Ext.extend(Ext.util.Observable, {
                }
        },
        /*
+        * Scroll the editor window to the current caret position
+        */
+       scrollToCaret: function () {
+               if (!Ext.isIE) {
+                       var e = this.getSelection().getParentElement(),
+                               w = this.iframe.getEl().dom.contentWindow ? this.iframe.getEl().dom.contentWindow : window,
+                               h = w.innerHeight || w.height,
+                               d = this.document,
+                               t = d.documentElement.scrollTop || d.body.scrollTop;
+                       if (e.offsetTop > h+t || e.offsetTop < t) {
+                               this.getSelection().getParentElement().scrollIntoView();
+                       }
+               }
+       },
+       /*
         * Add listeners
         */
        initEventsListening: function () {
@@ -2576,222 +2681,116 @@ HTMLArea.util.TYPO3 = function () {
                }
        }
 }();
-/***************************************************
- *  EDITOR UTILITIES
- ***************************************************/
-HTMLArea.getInnerText = function(el) {
-       var txt = '', i;
-       if(el.firstChild) {
-               for(i=el.firstChild;i;i =i.nextSibling) {
-                       if(i.nodeType == 3) txt += i.data;
-                       else if(i.nodeType == 1) txt += HTMLArea.getInnerText(i);
-               }
-       } else {
-               if(el.nodeType == 3) txt = el.data;
-       }
-       return txt;
-};
-
+ /*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
 HTMLArea.Editor.prototype.forceRedraw = function() {
+       this.appendToLog('HTMLArea.Editor', 'forceRedraw', 'Reference to deprecated method', 'warn');
        this.htmlArea.doLayout();
 };
-
-HTMLArea.Editor.prototype.updateToolbar = function(noStatus) {
-       this.toolbar.update(noStatus);
-};
-/***************************************************
- *  DOM TREE MANIPULATION
- ***************************************************/
-
 /*
  * Surround the currently selected HTML source code with the given tags.
  * Delete the selection, if any.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.surroundHTML = function(startTag,endTag) {
-       this.insertHTML(startTag + this.getSelectedHTML().replace(HTMLArea.Reg_body, "") + endTag);
+       this.appendToLog('HTMLArea.Editor', 'surroundHTML', 'Reference to deprecated method', 'warn');
+       this.getSelection().surroundHtml(startTag, endTag);
 };
 
 /*
  * Change the tag name of a node.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.convertNode = function(el,newTagName) {
-       var newel = this.document.createElement(newTagName), p = el.parentNode;
-       while (el.firstChild) newel.appendChild(el.firstChild);
-       p.insertBefore(newel, el);
-       p.removeChild(el);
-       return newel;
-};
-
-/*
- * Find a parent of an element with a specified tag
- */
-HTMLArea.getElementObject = function(el,tagName) {
-       var oEl = el;
-       while (oEl != null && oEl.nodeName.toLowerCase() != tagName) oEl = oEl.parentNode;
-       return oEl;
+       this.appendToLog('HTMLArea.Editor', 'surroundHTML', 'Reference to deprecated method', 'warn');
+       return HTMLArea.DOM.convertNode(el, newTagName);
 };
 
 /*
  * This function removes the given markup element
  *
- * @param      object  element: the inline element to be removed, content being preserved
+ * @param      object  element: the inline element to be removed, content and selection being preserved
  *
  * @return     void
- */
-HTMLArea.Editor.prototype.removeMarkup = function(element) {
-       var bookmark = this.getBookmark(this._createRange(this._getSelection()));
-       var parent = element.parentNode;
-       while (element.firstChild) {
-               parent.insertBefore(element.firstChild, element);
-       }
-       parent.removeChild(element);
-       this.selectRange(this.moveToBookmark(bookmark));
-};
-
-/*
- * This function verifies if the element has any allowed attributes
- *
- * @param      object  element: the DOM element
- * @param      array   allowedAttributes: array of allowed attribute names
  *
- * @return     boolean true if the element has one of the allowed attributes
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
-HTMLArea.hasAllowedAttributes = function(element,allowedAttributes) {
-       var value;
-       for (var i = allowedAttributes.length; --i >= 0;) {
-               value = element.getAttribute(allowedAttributes[i]);
-               if (value) {
-                       if (allowedAttributes[i] == "style" && element.style.cssText) {
-                               return true;
-                       } else {
-                               return true;
-                       }
-               }
-       }
-       return false;
+HTMLArea.Editor.prototype.removeMarkup = function(element) {
+       this.appendToLog('HTMLArea.Editor', 'removeMarkup', 'Reference to deprecated method', 'warn');
+       this.getDomNode().removeMarkup(element);
 };
-
-/***************************************************
- *  SELECTIONS AND RANGES
- ***************************************************/
-
 /*
  * Return true if we have some selected content
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.hasSelectedText = function() {
-       return this.getSelectedHTML() != "";
+       this.appendToLog('HTMLArea.Editor', 'hasSelectedText', 'Reference to deprecated method', 'warn');
+       return !this.getSelection().isEmpty();
 };
 
 /*
- * Get an array with all the ancestor nodes of the selection.
+ * Get an array with all the ancestor nodes of the selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.getAllAncestors = function() {
-       var p = this.getParentElement();
-       var a = [];
-       while (p && (p.nodeType === 1) && (p.nodeName.toLowerCase() !== "body")) {
-               a.push(p);
-               p = p.parentNode;
-       }
-       a.push(this.document.body);
-       return a;
-};
-
-/*
- * Get the block ancestors of an element within a given block
- */
-HTMLArea.Editor.prototype.getBlockAncestors = function(element, withinBlock) {
-       var ancestors = new Array();
-       var ancestor = element;
-       while (ancestor && (ancestor.nodeType === 1) && !/^(body)$/i.test(ancestor.nodeName) && ancestor != withinBlock) {
-               if (HTMLArea.isBlockElement(ancestor)) {
-                       ancestors.unshift(ancestor);
-               }
-               ancestor = ancestor.parentNode;
-       }
-       ancestors.unshift(ancestor);
-       return ancestors;
+       this.appendToLog('HTMLArea.Editor', 'getAllAncestors', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getAllAncestors();
 };
 
 /*
  * Get the block elements containing the start and the end points of the selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.getEndBlocks = function(selection) {
-       var range = this._createRange(selection);
-       if (!Ext.isIE) {
-               var parentStart = range.startContainer;
-               if (/^(body)$/i.test(parentStart.nodeName)) {
-                       parentStart = parentStart.firstChild;
-               }
-               var parentEnd = range.endContainer;
-               if (/^(body)$/i.test(parentEnd.nodeName)) {
-                       parentEnd = parentEnd.lastChild;
-               }
-       } else {
-               if (selection.type !== "Control" ) {
-                       var rangeEnd = range.duplicate();
-                       range.collapse(true);
-                       var parentStart = range.parentElement();
-                       rangeEnd.collapse(false);
-                       var parentEnd = rangeEnd.parentElement();
-               } else {
-                       var parentStart = range.item(0);
-                       var parentEnd = parentStart;
-               }
-       }
-       while (parentStart && !HTMLArea.isBlockElement(parentStart)) {
-               parentStart = parentStart.parentNode;
-       }
-       while (parentEnd && !HTMLArea.isBlockElement(parentEnd)) {
-               parentEnd = parentEnd.parentNode;
-       }
-       return {        start   : parentStart,
-                       end     : parentEnd
-       };
+       this.appendToLog('HTMLArea.Editor', 'getEndBlocks', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getEndBlocks();
 };
 
 /*
  * This function determines if the end poins of the current selection are within the same block
  *
  * @return     boolean true if the end points of the current selection are inside the same block element
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.endPointsInSameBlock = function() {
-       var selection = this._getSelection();
-       if (this._selectionEmpty(selection)) {
-               return true;
-       } else {
-               var parent = this.getParentElement(selection);
-               var endBlocks = this.getEndBlocks(selection);
-               return (endBlocks.start === endBlocks.end && !/^(table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
-       }
+       this.appendToLog('HTMLArea.Editor', 'endPointsInSameBlock', 'Reference to deprecated method', 'warn');
+       return this.getSelection().endPointsInSameBlock();
 };
 
 /*
  * Get the deepest ancestor of the selection that is of the specified type
  * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype._getFirstAncestor = function(sel,types) {
-       var prnt = this._activeElement(sel);
-       if (prnt == null) {
-               try {
-                       prnt = (Ext.isIE ? this._createRange(sel).parentElement() : this._createRange(sel).commonAncestorContainer);
-               } catch(e) {
-                       return null;
-               }
-       }
-       if (typeof(types) == 'string') types = [types];
-
-       while (prnt) {
-               if (prnt.nodeType == 1) {
-                       if (types == null) return prnt;
-                       for (var i = 0; i < types.length; i++) {
-                               if(prnt.tagName.toLowerCase() == types[i]) return prnt;
-                       }
-                       if(prnt.tagName.toLowerCase() == 'body') break;
-                       if(prnt.tagName.toLowerCase() == 'table') break;
-               }
-               prnt = prnt.parentNode;
-       }
-       return null;
+       this.appendToLog('HTMLArea.Editor', '_getFirstAncestor', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getFirstAncestorOfType(types);
 };
 /*
  * Get the node whose contents are currently fully selected
@@ -2801,128 +2800,67 @@ HTMLArea.Editor.prototype._getFirstAncestor = function(sel,types) {
  * @param      array           ancestors: the array of ancestors node of the current selection
  *
  * @return     object          the fully selected node, if any, null otherwise
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.getFullySelectedNode = function (selection, range, ancestors) {
-       var node, fullNodeSelected = false;
-       if (!selection) {
-               var selection = this._getSelection();
-       }
-       if (!this._selectionEmpty(selection)) {
-               if (!range) {
-                       var range = this._createRange(selection);
-               }
-               if (!ancestors) {
-                       var ancestors = this.getAllAncestors();
-               }
-               Ext.each(ancestors, function (ancestor) {
-                       if (Ext.isIE) {
-                               fullNodeSelected = (selection.type !== 'Control' && ancestor.innerText == range.text) || (selection.type === 'Control' && ancestor.innerText == range.item(0).text);
-                       } else {
-                               fullNodeSelected = (ancestor.textContent == range.toString());
-                       }
-                       if (fullNodeSelected) {
-                               node = ancestor;
-                               return false;
-                       }
-               });
-                       // Working around bug with WebKit selection
-               if (Ext.isWebKit && !fullNodeSelected) {
-                       var statusBarSelection = this.statusBar ? this.statusBar.getSelection() : null;
-                       if (statusBarSelection && statusBarSelection.textContent == range.toString()) {
-                               fullNodeSelected = true;
-                               node = statusBarSelection;
-                       }
-               }
-       }
-       return fullNodeSelected ? node : null;
+       this.appendToLog('HTMLArea.Editor', 'getFullySelectedNode', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getFullySelectedNode();
 };
-/***************************************************
- *  Category: EVENT HANDLERS
- ***************************************************/
-
 /*
  * Intercept some native execCommand commands
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
 HTMLArea.Editor.prototype.execCommand = function(cmdID, UI, param) {
-       this.focus();
-       switch (cmdID) {
-               default:
-                       try {
-                               this.document.execCommand(cmdID, UI, param);
-                       } catch(e) {
-                               this.appendToLog('HTMLArea.Editor', 'execCommand', e + ' by execCommand(' + cmdID + ')', 'error');
-                       }
-       }
-       this.toolbar.update();
-       return false;
+       this.appendToLog('HTMLArea.Editor', 'execCommand', 'Reference to deprecated method', 'warn');
+       return this.getSelection().execCommand(cmdID, UI, param);
 };
 
-HTMLArea.Editor.prototype.scrollToCaret = function() {
-       if (!Ext.isIE) {
-               var e = this.getParentElement(),
-                       w = this._iframe.contentWindow ? this._iframe.contentWindow : window,
-                       h = w.innerHeight || w.height,
-                       d = this.document,
-                       t = d.documentElement.scrollTop || d.body.scrollTop;
-               if (e.offsetTop > h+t || e.offsetTop < t) {
-                       this.getParentElement().scrollIntoView();
-               }
-       }
-};
 /***************************************************
  *  UTILITY FUNCTIONS
  ***************************************************/
-
+Ext.apply(HTMLArea.util, {
+       /*
+        * Perform HTML encoding of some given string
+        * Borrowed in part from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
+        */
+       htmlDecode: function (str) {
+               str = str.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
+               str = str.replace(/&nbsp;/g, '\xA0'); // Decimal 160, non-breaking-space
+               str = str.replace(/&quot;/g, '\x22');
+               str = str.replace(/&#39;/g, "'") ;
+               str = str.replace(/&amp;/g, '&');
+               return str;
+       },
+       htmlEncode: function (str) {
+               if (typeof(str) != 'string') {
+                       str = str.toString();
+               }
+               str = str.replace(/&/g, '&amp;');
+               str = str.replace(/</g, '&lt;').replace(/>/g, '&gt;');
+               str = str.replace(/\xA0/g, '&nbsp;'); // Decimal 160, non-breaking-space
+               str = str.replace(/\x22/g, '&quot;'); // \x22 means '"'
+               return str;
+       }
+});
 /*
- * Check if the client agent is supported
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
-HTMLArea.checkSupportedBrowser = function() {
-       return Ext.isGecko || Ext.isWebKit || Ext.isOpera || Ext.isIE;
-};
-
-HTMLArea.isBlockElement = function(el) { return el && el.nodeType == 1 && HTMLArea.RE_blockTags.test(el.nodeName.toLowerCase()); };
-HTMLArea.needsClosingTag = function(el) { return el && el.nodeType == 1 && !HTMLArea.RE_noClosingTag.test(el.tagName.toLowerCase()); };
-
+HTMLArea.htmlDecode = HTMLArea.util.htmlDecode;
 /*
- * Perform HTML encoding of some given string
- * Borrowed in part from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
  */
-HTMLArea.htmlDecode = function(str) {
-       str = str.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
-       str = str.replace(/&nbsp;/g, "\xA0"); // Decimal 160, non-breaking-space
-       str = str.replace(/&quot;/g, "\x22");
-       str = str.replace(/&#39;/g, "'") ;
-       str = str.replace(/&amp;/g, "&");
-       return str;
-};
-HTMLArea.htmlEncode = function(str) {
-       if (typeof(str) != 'string') str = str.toString(); // we don't need regexp for that, but.. so be it for now.
-       str = str.replace(/&/g, "&amp;");
-       str = str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
-       str = str.replace(/\xA0/g, "&nbsp;"); // Decimal 160, non-breaking-space
-       str = str.replace(/\x22/g, "&quot;"); // \x22 means '"'
-       return str;
-};
-HTMLArea.getPrevNode = function(node) {
-       if(!node)                return null;
-       if(node.previousSibling) return node.previousSibling;
-       if(node.parentNode)      return node.parentNode;
-       return null;
-};
-
-HTMLArea.getNextNode = function(node) {
-       if(!node)            return null;
-       if(node.nextSibling) return node.nextSibling;
-       if(node.parentNode)  return node.parentNode;
-       return null;
-};
+HTMLArea.htmlEncode = HTMLArea.util.htmlEncode;
 
-HTMLArea.removeFromParent = function(el) {
-       if(!el.parentNode) return;
-       var pN = el.parentNode;
-       pN.removeChild(el);
-       return el;
-};
 /*****************************************************************
  * HTMLArea.DOM: Utility functions for dealing with the DOM tree *
  *****************************************************************/
@@ -2944,6 +2882,36 @@ HTMLArea.DOM = function () {
                DOCUMENT_TYPE_NODE: 10,
                DOCUMENT_FRAGMENT_NODE: 11,
                NOTATION_NODE: 12,
+               /***************************************************
+               *  DOM-RELATED REGULAR EXPRESSIONS
+               ***************************************************/
+               RE_blockTags: /^(body|p|h1|h2|h3|h4|h5|h6|ul|ol|pre|dl|dt|dd|div|noscript|blockquote|form|hr|table|caption|fieldset|address|td|tr|th|li|tbody|thead|tfoot|iframe)$/i,
+               RE_closingTags: /^(p|blockquote|a|li|ol|ul|dl|dt|td|th|tr|tbody|thead|tfoot|caption|colgroup|table|div|b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|label|q|samp|small|span|strike|strong|sub|sup|tt|u|var|abbr|acronym|font|center|object|embed|style|script|title|head)$/i,
+               RE_noClosingTag: /^(img|br|hr|col|input|area|base|link|meta|param)$/i,
+               RE_bodyTag: new RegExp('<\/?(body)[^>]*>', 'gi'),
+               /***************************************************
+               *  STATIC METHODS ON DOM NODE
+               ***************************************************/
+               /*
+                * Determine whether an element node is a block element
+                *
+                * @param       object          element: the element node
+                *
+                * @return      boolean         true, if the element node is a block element
+                */
+               isBlockElement: function (element) {
+                       return element && element.nodeType === HTMLArea.DOM.ELEMENT_NODE && HTMLArea.DOM.RE_blockTags.test(element.nodeName);
+               },
+               /*
+                * Determine whether an element node needs a closing tag
+                *
+                * @param       object          element: the element node
+                *
+                * @return      boolean         true, if the element node needs a closing tag
+                */
+               needsClosingTag: function (element) {
+                       return element && element.nodeType === HTMLArea.DOM.ELEMENT_NODE && !HTMLArea.DOM.RE_noClosingTag.test(element.nodeName);
+               },
                /*
                 * Gets the class names assigned to a node, reserved classes removed
                 *
@@ -3048,6 +3016,168 @@ HTMLArea.DOM = function () {
                        }
                },
                /*
+                * Get the innerText of a given node
+                *
+                * @param       object          node: the node
+                *
+                * @return      string          the text inside the node
+                */
+               getInnerText: function (node) {
+                       return (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) ? node.innerText : node.textContent;;
+               },
+               /*
+                * Get the block ancestors of a node within a given block
+                *
+                * @param       object          node: the given node
+                * @param       object          withinBlock: the containing node
+                *
+                * @return      array           array of block ancestors
+                */
+               getBlockAncestors: function (node, withinBlock) {
+                       var ancestors = [];
+                       var ancestor = node;
+                       while (ancestor && (ancestor.nodeType === HTMLArea.DOM.ELEMENT_NODE) && !/^(body)$/i.test(ancestor.nodeName) && ancestor != withinBlock) {
+                               if (HTMLArea.DOM.isBlockElement(ancestor)) {
+                                       ancestors.unshift(ancestor);
+                               }
+                               ancestor = ancestor.parentNode;
+                       }
+                       ancestors.unshift(ancestor);
+                       return ancestors;
+               },
+               /*
+                * Get the deepest element ancestor of a given node that is of one of the specified types
+                *
+                * @param       object          node: the given node
+                * @param       array           types: an array of nodeNames
+                *
+                * @return      object          the found ancestor of one of the given types or null
+                */
+               getFirstAncestorOfType: function (node, types) {
+                       var ancestor = null,
+                               parent = node;
+                       if (!Ext.isEmpty(types)) {
+                               if (Ext.isString(types)) {
+                                       var types = [types];
+                               }
+                               types = new RegExp( '^(' + types.join('|') + ')$', 'i');
+                               while (parent && parent.nodeType === HTMLArea.DOM.ELEMENT_NODE && !/^(body)$/i.test(parent.nodeName)) {
+                                       if (types.test(parent.nodeName)) {
+                                               ancestor = parent;
+                                               break;
+                                       }
+                                       parent = parent.parentNode;
+                               }
+                       }
+                       return ancestor;
+               },
+               /*
+                * Determine whether a given node has any allowed attributes
+                *
+                * @param       object          node: the DOM node
+                * @param       array           allowedAttributes: array of allowed attribute names
+                *
+                * @return      boolean         true if the node has one of the allowed attributes
+                */
+                hasAllowedAttributes: function (node, allowedAttributes) {
+                       var value,
+                               hasAllowedAttributes = false;
+                       if (Ext.isString(allowedAttributes)) {
+                               allowedAttributes = [allowedAttributes];
+                       }
+                       allowedAttributes = allowedAttributes || [];
+                       for (var i = allowedAttributes.length; --i >= 0;) {
+                               value = node.getAttribute(allowedAttributes[i]);
+                               if (value) {
+                                       if (allowedAttributes[i] === 'style') {
+                                               if (node.style.cssText) {
+                                                       hasAllowedAttributes = true;
+                                                       break;
+                                               }
+                                       } else {
+                                               hasAllowedAttributes = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       return hasAllowedAttributes;
+               },
+               /*
+                * Remove the given node from its parent
+                *
+                * @param       object          node: the DOM node
+                *
+                * @return      void
+                */
+               removeFromParent: function (node) {
+                       var parent = node.parentNode;
+                       if (parent) {
+                               parent.removeChild(node);
+                       }
+               },
+               /*
+                * Change the nodeName of an element node
+                *
+                * @param       object          node: the node to convert (must belong to a document)
+                * @param       string          nodeName: the nodeName of the converted node
+                *
+                * @retrun      object          the converted node or the input node
+                */
+               convertNode: function (node, nodeName) {
+                       var convertedNode = node,
+                               ownerDocument = node.ownerDocument;
+                       if (ownerDocument && node.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
+                               var convertedNode = ownerDocument.createElement(nodeName),
+                                       parent = node.parentNode;
+                               while (node.firstChild) {
+                                       convertedNode.appendChild(node.firstChild);
+                               }
+                               parent.insertBefore(convertedNode, node);
+                               parent.removeChild(node);
+                       }
+                       return convertedNode;
+               },
+               /*
+                * Determine whether a given range intersects a given node
+                *
+                * @param       object          range: the range
+                * @param       object          node: the DOM node (must belong to a document)
+                *
+                * @return      boolean         true if the range intersects the node
+                */
+               rangeIntersectsNode: function (range, node) {
+                       var rangeIntersectsNode = false,
+                               ownerDocument = node.ownerDocument;
+                       if (ownerDocument) {
+                               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                                       var nodeRange = ownerDocument.body.createTextRange();
+                                       nodeRange.moveToElementText(node);
+                                       rangeIntersectsNode = (range.compareEndPoints('EndToStart', nodeRange) == -1 && range.compareEndPoints('StartToEnd', nodeRange) == 1) ||
+                                               (range.compareEndPoints('EndToStart', nodeRange) == 1 && range.compareEndPoints('StartToEnd', nodeRange) == -1);
+                               } else {
+                                       var nodeRange = ownerDocument.createRange();
+                                       try {
+                                               nodeRange.selectNode(node);
+                                       } catch (e) {
+                                               if (Ext.isWebKit) {
+                                                       nodeRange.setStart(node, 0);
+                                                       if (node.nodeType === HTMLArea.DOM.TEXT_NODE || node.nodeType === HTMLArea.DOM.COMMENT_NODE || node.nodeType === HTMLArea.DOM.CDATA_SECTION_NODE) {
+                                                               nodeRange.setEnd(node, node.textContent.length);
+                                                       } else {
+                                                               nodeRange.setEnd(node, node.childNodes.length);
+                                                       }
+                                               } else {
+                                                       nodeRange.selectNodeContents(node);
+                                               }
+                                       }
+                                               // Note: sometimes WebKit inverts the end points
+                                       rangeIntersectsNode = (range.compareBoundaryPoints(range.END_TO_START, nodeRange) == -1 && range.compareBoundaryPoints(range.START_TO_END, nodeRange) == 1) ||
+                                               (range.compareBoundaryPoints(range.END_TO_START, nodeRange) == 1 && range.compareBoundaryPoints(range.START_TO_END, nodeRange) == -1);
+                               }
+                       }
+                       return rangeIntersectsNode;
+               },
+               /*
                 * Make url's absolute in the DOM tree under the root node
                 *
                 * @param       object          root: the root node
@@ -3191,7 +3321,7 @@ HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
                                }
                                break;
                        case HTMLArea.DOM.TEXT_NODE:
-                               html += /^(script|style)$/i.test(node.parentNode.nodeName) ? node.data : HTMLArea.htmlEncode(node.data);
+                               html += /^(script|style)$/i.test(node.parentNode.nodeName) ? node.data : HTMLArea.util.htmlEncode(node.data);
                                break;
                        case HTMLArea.DOM.ENTITY_NODE:
                                html += node.nodeValue;
@@ -3223,7 +3353,7 @@ HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
         */
        renderNodeEnd: function (node) {
                var html = '';
-               if (node.nodeType == HTMLArea.DOM.ELEMENT_NODE) {
+               if (node.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
                        if (this.keepTags.test(node.nodeName) && !this.removeTags.test(node.nodeName)) {
                                html += this.setClosingTag(node);
                        }
@@ -3298,7 +3428,7 @@ HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
                                return html;
                                // In Gecko, whenever some text is entered in an empty block, a trailing br tag is added by the browser.
                                // If the br element is a trailing br in a block element with no other content or with content other than a br, it may be configured to be removed
-                       } else if (this.removeTrailingBR && !node.nextSibling && HTMLArea.isBlockElement(node.parentNode) && (!node.previousSibling || !/^br$/i.test(node.previousSibling.nodeName))) {
+                       } else if (this.removeTrailingBR && !node.nextSibling && HTMLArea.DOM.isBlockElement(node.parentNode) && (!node.previousSibling || !/^br$/i.test(node.previousSibling.nodeName))) {
                                                // If an empty paragraph with a class attribute, insert a non-breaking space so that RTE transform does not clean it away
                                        if (!node.previousSibling && node.parentNode && /^p$/i.test(node.parentNode.nodeName) && node.parentNode.className) {
                                                html += "&nbsp;";
@@ -3309,7 +3439,7 @@ HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
                        // Normal node
                var attributes = this.getAttributes(node);
                for (var i = 0, n = attributes.length; i < n; i++) {
-                       html +=  ' ' + attributes[i]['attributeName'] + '="' + HTMLArea.htmlEncode(attributes[i]['attributeValue']) + '"';
+                       html +=  ' ' + attributes[i]['attributeName'] + '="' + HTMLArea.util.htmlEncode(attributes[i]['attributeValue']) + '"';
                }
                html = '<' + node.nodeName.toLowerCase() + html + (HTMLArea.RE_noClosingTag.test(node.nodeName) ? ' />' : '>');
                        // Fix orphan list elements
@@ -3344,7 +3474,1666 @@ HTMLArea.DOM.Walker = Ext.extend(HTMLArea.DOM.Walker, {
        }
 });
 /***************************************************
- *  HTMLArea.CSS.Parser: CSS Parser
+ *  HTMLArea.DOM.Selection: Selection object
+ ***************************************************/
+HTMLArea.DOM.Selection = function (config) {
+};
+HTMLArea.DOM.Selection = Ext.extend(HTMLArea.DOM.Selection, {
+       /*
+        * Reference to the editor MUST be set in config
+        */
+       editor: null,
+       /*
+        * Reference to the editor document
+        */
+       document: null,
+       /*
+        * Reference to the editor iframe window
+        */
+       window: null,
+       /*
+        * The current selection
+        */
+       selection: null,
+       /*
+        * HTMLArea.DOM.Selection constructor
+        */
+       constructor: function (config) {
+                       // Apply config
+               Ext.apply(this, config);
+                       // Initialize references
+               this.document = this.editor.document;
+               this.window = this.editor.iframe.getEl().dom.contentWindow;
+                       // Set current selection
+               this.get();
+       },
+       /*
+        * Get the current selection object
+        *
+        * @return      object          this
+        */             
+       get: function () {
+               this.editor.focus();
+               this.selection = this.window.getSelection ? this.window.getSelection() : this.document.selection;
+               return this;
+       },
+       /*
+        * Get the type of the current selection
+        *
+        * @return      string          the type of selection ("None", "Text" or "Control")
+        */
+       getType: function() {
+                       // By default set the type to "Text"
+               var type = 'Text';
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isFunction(this.selection.getRangeAt)) {
+                                       // Check if the current selection is a Control
+                               if (this.selection && this.selection.rangeCount == 1) {
+                                       var range = this.selection.getRangeAt(0);
+                                       if (range.startContainer.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
+                                               if (
+                                                               // Gecko
+                                                       (range.startContainer == range.endContainer && (range.endOffset - range.startOffset) == 1) ||
+                                                               // Opera and WebKit
+                                                       (range.endContainer.nodeType === HTMLArea.DOM.TEXT_NODE && range.endOffset == 0 && range.startContainer.childNodes[range.startOffset].nextSibling == range.endContainer)
+                                               ) {
+                                                       if (/^(img|hr|li|table|tr|td|embed|object|ol|ul|dl)$/i.test(range.startContainer.childNodes[range.startOffset].nodeName)) {
+                                                               type = 'Control';
+                                                       }
+                                               }
+                                       }
+                               }
+                       } else {
+                                       // IE8 or IE7
+                               type = this.selection.type;
+                       }
+               }
+               return type;
+       },
+       /*
+        * Empty the current selection
+        *
+        * @return      object          this
+        */
+       empty: function () {
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isFunction(this.selection.removeAllRanges)) {
+                               this.selection.removeAllRanges();
+                       } else {
+                                       // IE8, IE7 or old version of WebKit
+                               this.selection.empty();
+                       }
+                       if (Ext.isOpera) {
+                               this.editor.focus();
+                       }
+               }
+               return this;
+       },
+       /*
+        * Determine whether the current selection is empty or not
+        *
+        * @return      boolean         true, if the selection is empty
+        */
+       isEmpty: function () {
+               var isEmpty = true;
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                               switch (this.selection.type) {
+                                       case 'None':
+                                               isEmpty = true;
+                                               break;
+                                       case 'Text':
+                                               isEmpty = !this.createRange().text;
+                                               break;
+                                       default:
+                                               isEmpty = !this.createRange().htmlText;
+                                               break;
+                               }
+                       } else {
+                               isEmpty = this.selection.isCollapsed;
+                       }
+               }
+               return isEmpty;
+       },
+       /*
+        * Get a range corresponding to the current selection
+        *
+        * @return      object          the range of the selection
+        */
+       createRange: function () {
+               var range;
+               this.get();
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       range = this.selection.createRange();
+               } else {
+                       if (Ext.isEmpty(this.selection)) {
+                               range = this.document.createRange();
+                       } else {
+                                       // Older versions of WebKit did not support getRangeAt
+                               if (Ext.isWebKit && !Ext.isFunction(this.selection.getRangeAt)) {
+                                       range = this.document.createRange();
+                                       if (this.selection.baseNode == null) {
+                                               range.setStart(this.document.body, 0);
+                                               range.setEnd(this.document.body, 0);
+                                       } else {
+                                               range.setStart(this.selection.baseNode, this.selection.baseOffset);
+                                               range.setEnd(this.selection.extentNode, this.selection.extentOffset);
+                                               if (range.collapsed != this.selection.isCollapsed) {
+                                                       range.setStart(this.selection.extentNode, this.selection.extentOffset);
+                                                       range.setEnd(this.selection.baseNode, this.selection.baseOffset);
+                                               }
+                                       }
+                               } else {
+                                       try {
+                                               range = this.selection.getRangeAt(0);
+                                       } catch (e) {
+                                               range = this.document.createRange();
+                                       }
+                               }
+                       }
+               }
+               return range;
+       },
+       /*
+        * Return the ranges of the selection
+        *
+        * @return      array           array of ranges
+        */
+       getRanges: function () {
+               this.get();
+               var ranges = [];
+                       // Older versions of WebKit, IE7 and IE8 did not support getRangeAt
+               if (!Ext.isEmpty(this.selection) && Ext.isFunction(this.selection.getRangeAt)) {
+                       for (var i = this.selection.rangeCount; --i >= 0;) {
+                               ranges.push(this.selection.getRangeAt(i));
+                       }
+               } else {
+                       ranges.push(this.createRange());
+               }
+               return ranges;
+       },
+       /*
+        * Add a range to the selection
+        *
+        * @param       object          range: the range to be added to the selection
+        *
+        * @return      object          this
+        */
+       addRange: function (range) {
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isFunction(this.selection.addRange)) {
+                               this.selection.addRange(range);
+                       } else if (Ext.isWebKit) {
+                               this.selection.setBaseAndExtent(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+                       }
+               }
+               return this;
+       },
+       /*
+        * Set the ranges of the selection
+        *
+        * @param       array           ranges: array of range to be added to the selection
+        *
+        * @return      object          this
+        */
+       setRanges: function (ranges) {
+               this.get();
+               this.empty();
+               for (var i = ranges.length; --i >= 0;) {
+                       this.addRange(ranges[i]);
+               }
+               return this;
+       },
+       /*
+        * Set the selection to a given range
+        *
+        * @param       object          range: the range to be selected
+        *
+        * @return      object          this
+        */
+       selectRange: function (range) {
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isFunction(this.selection.getRangeAt)) {
+                               this.empty().addRange(range);
+                       } else {
+                                       // IE8 or IE7
+                               range.select();
+                       }
+               }
+               return this;
+       },
+       /*
+        * Set the selection to a given node
+        *
+        * @param       object          node: the node to be selected
+        * @param       boolean         endPoint: collapse the selection at the start point (true) or end point (false) of the node
+        *
+        * @return      object          this
+        */
+       selectNode: function (node, endPoint) {
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                                       // IE8/7/6 cannot set this type of selection
+                               this.selectNodeContents(node, endPoint);
+                       } else if (Ext.isWebKit && /^(img)$/i.test(node.nodeName)) {
+                               this.selection.setBaseAndExtent(node, 0, node, 1);
+                       } else {
+                               var range = this.document.createRange();
+                               if (node.nodeType === HTMLArea.DOM.ELEMENT_NODE && /^(body)$/i.test(node.nodeName)) {
+                                       if (Ext.isWebKit) {
+                                               range.setStart(node, 0);
+                                               range.setEnd(node, node.childNodes.length);
+                                       } else {
+                                               range.selectNodeContents(node);
+                                       }
+                               } else {
+                                       range.selectNode(node);
+                               }
+                               if (typeof(endPoint) !== 'undefined') {
+                                       range.collapse(endPoint);
+                               }
+                               this.selectRange(range);
+                       }
+               }
+               return this;
+       },
+       /*
+        * Set the selection to the inner contents of a given node
+        *
+        * @param       object          node: the node of which the contents are to be selected
+        * @param       boolean         endPoint: collapse the selection at the start point (true) or end point (false)
+        *
+        * @return      object          this
+        */
+       selectNodeContents: function (node, endPoint) {
+               var range;
+               this.get();
+               if (!Ext.isEmpty(this.selection)) {
+                       if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                               range = this.document.body.createTextRange();
+                               range.moveToElementText(node);
+                       } else {
+                               range = this.document.createRange();
+                               if (Ext.isWebKit) {
+                                       range.setStart(node, 0);
+                                       if (node.nodeType === HTMLArea.DOM.TEXT_NODE || node.nodeType === HTMLArea.DOM.COMMENT_NODE || node.nodeType === HTMLArea.DOM.CDATA_SECTION_NODE) {
+                                               range.setEnd(node, node.textContent.length);
+                                       } else {
+                                               range.setEnd(node, node.childNodes.length);
+                                       }
+                               } else {
+                                       range.selectNodeContents(node);
+                               }
+                       }
+                       if (typeof(endPoint) !== 'undefined') {
+                               range.collapse(endPoint);
+                       }
+                       this.selectRange(range);
+               }
+               return this;
+       },
+       /*
+        * Get the deepest node that contains both endpoints of the current selection.
+        *
+        * @return      object          the deepest node that contains both endpoints of the current selection. 
+        */
+       getParentElement: function () {
+               var parentElement,
+                       range;
+               this.get();
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       range = this.createRange();
+                       switch (this.selection.type) {
+                               case 'Text':
+                               case 'None':
+                                       parentElement = range.parentElement();
+                                       if (/^(form)$/i.test(parentElement.nodeName)) {
+                                               parentElement = this.document.body;
+                                       } else if (/^(li)$/i.test(parentElement.nodeName) && range.htmlText.replace(/\s/g, '') == parentElement.parentNode.outerHTML.replace(/\s/g, '')) {
+                                               parentElement = parentElement.parentNode;
+                                       }
+                                       break;
+                               case 'Control':
+                                       parentElement = range.item(0);
+                                       break;
+                               default:
+                                       parentElement = this.document.body;
+                                       break;
+                       }
+               } else {
+                       if (this.getType() === 'Control') {
+                               parentElement = this.getElement();
+                       } else {
+                               range = this.createRange();
+                               parentElement = range.commonAncestorContainer;
+                                       // Firefox 3 may report the document as commonAncestorContainer
+                               if (parentElement.nodeType === HTMLArea.DOM.DOCUMENT_NODE) {
+                                       parentElement = this.document.body;
+                               } else {
+                                       while (parentElement && parentElement.nodeType === HTMLArea.DOM.TEXT_NODE) {
+                                               parentElement = parentElement.parentNode;
+                                       }
+                               }
+                       }
+               }
+               return parentElement;
+       },
+       /*
+        * Get the selected element (if any), in the case that a single element (object like and image or a table) is selected
+        * In IE language, we have a range of type 'Control'
+        *
+        * @return      object          the selected node
+        */
+       getElement: function () {
+               var element = null;
+               this.get();
+               if (!Ext.isEmpty(this.selection) && this.selection.anchorNode && this.selection.anchorNode.nodeType === HTMLArea.DOM.ELEMENT_NODE && this.getType() == 'Control') {
+                       element = this.selection.anchorNode.childNodes[this.selection.anchorOffset];
+                               // For Safari, the anchor node for a control selection is the control itself
+                       if (!element) {
+                               element = this.selection.anchorNode;
+                       } else if (element.nodeType !== HTMLArea.DOM.ELEMENT_NODE) {
+                               element = null;
+                       }
+               }
+               return element;
+       },
+       /*
+        * Get the deepest element ancestor of the selection that is of one of the specified types
+        *
+        * @param       array           types: an array of nodeNames
+        *
+        * @return      object          the found ancestor of one of the given types or null
+        */
+       getFirstAncestorOfType: function (types) {
+               var node = this.getParentElement();
+               return HTMLArea.DOM.getFirstAncestorOfType(node, types);
+       },
+       /*
+        * Get an array with all the ancestor nodes of the current selection
+        *
+        * @return      array           the ancestor nodes
+        */
+       getAllAncestors: function () {
+               var parent = this.getParentElement(),
+                       ancestors = [];
+               while (parent && parent.nodeType === HTMLArea.DOM.ELEMENT_NODE && !/^(body)$/i.test(parent.nodeName)) {
+                       ancestors.push(parent);
+                       parent = parent.parentNode;
+               }
+               ancestors.push(this.document.body);
+               return ancestors;
+       },
+       /*
+        * Get an array with the parent elements of a multiple selection
+        *
+        * @return      array           the selected elements
+        */
+       getElements: function () {
+               var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null,
+                       elements = [];
+               if (statusBarSelection) {
+                       elements.push(statusBarSelection);
+               } else {
+                       var ranges = this.getRanges();
+                               parent;
+                       if (ranges.length > 1) {
+                               for (var i = ranges.length; --i >= 0;) {
+                                       parent = range[i].commonAncestorContainer;
+                                               // Firefox 3 may report the document as commonAncestorContainer
+                                       if (parent.nodeType === HTMLArea.DOM.DOCUMENT_NODE) {
+                                               parent = this.document.body;
+                                       } else {
+                                               while (parent && parent.nodeType === HTMLArea.DOM.TEXT_NODE) {
+                                                       parent = parent.parentNode;
+                                               }
+                                       }
+                                       elements.push(parent);
+                               }
+                       } else {
+                               elements.push(this.getParentElement());
+                       }
+               }
+               return elements;
+       },
+       /*
+        * Get the node whose contents are currently fully selected
+        *
+        * @return      object          the fully selected node, if any, null otherwise
+        */
+       getFullySelectedNode: function () {
+               var node = null,
+                       isFullySelected = false;
+               this.get();
+               if (!this.isEmpty()) {
+                       var type = this.getType();
+                       var range = this.createRange();
+                       var ancestors = this.getAllAncestors();
+                       Ext.each(ancestors, function (ancestor) {
+                               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                                       isFullySelected = (type !== 'Control' && ancestor.innerText == range.text) || (type === 'Control' && ancestor.innerText == range.item(0).text);
+                               } else {
+                                       isFullySelected = (ancestor.textContent == range.toString());
+                               }
+                               if (isFullySelected) {
+                                       node = ancestor;
+                                       return false;
+                               }
+                       });
+                               // Working around bug with WebKit selection
+                       if (Ext.isWebKit && !isFullySelected) {
+                               var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
+                               if (statusBarSelection && statusBarSelection.textContent == range.toString()) {
+                                       isFullySelected = true;
+                                       node = statusBarSelection;
+                               }
+                       }
+               }
+               return node;
+       },
+       /*
+        * Get the block elements containing the start and the end points of the selection
+        *
+        * @return      object          object with properties start and end set to the end blocks of the selection
+        */
+       getEndBlocks: function () {
+               var range = this.createRange(),
+                       parentStart,
+                       parentEnd;
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       if (this.getType() === 'Control') {
+                               parentStart = range.item(0);
+                               parentEnd = parentStart;
+                       } else {
+                               var rangeEnd = range.duplicate();
+                               range.collapse(true);
+                               parentStart = range.parentElement();
+                               rangeEnd.collapse(false);
+                               parentEnd = rangeEnd.parentElement();
+                       }
+               } else {
+                       parentStart = range.startContainer;
+                       if (/^(body)$/i.test(parentStart.nodeName)) {
+                               parentStart = parentStart.firstChild;
+                       }
+                       parentEnd = range.endContainer;
+                       if (/^(body)$/i.test(parentEnd.nodeName)) {
+                               parentEnd = parentEnd.lastChild;
+                       }
+               }
+               while (parentStart && !HTMLArea.DOM.isBlockElement(parentStart)) {
+                       parentStart = parentStart.parentNode;
+               }
+               while (parentEnd && !HTMLArea.DOM.isBlockElement(parentEnd)) {
+                       parentEnd = parentEnd.parentNode;
+               }
+               return {
+                       start: parentStart,
+                       end: parentEnd
+               };
+       },
+       /*
+        * Determine whether the end poins of the current selection are within the same block
+        *
+        * @return      boolean         true if the end points of the current selection are in the same block
+        */
+       endPointsInSameBlock: function() {
+               var endPointsInSameBlock = true;
+               this.get();
+               if (!this.isEmpty()) {
+                       var parent = this.getParentElement();
+                       var endBlocks = this.getEndBlocks();
+                       endPointsInSameBlock = (endBlocks.start === endBlocks.end && !/^(table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
+               }
+               return endPointsInSameBlock;
+       },
+       /*
+        * Retrieve the HTML contents of the current selection
+        *
+        * @return      string          HTML text of the current selection
+        */
+       getHtml: function () {
+               var range = this.createRange(),
+                       html = '';
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       if (this.getType() === 'Control') {
+                                       // We have a controlRange collection
+                               var bodyRange = this.document.body.createTextRange();
+                               bodyRange.moveToElementText(range(0));
+                               html = bodyRange.htmlText;
+                       } else {
+                               html = range.htmlText;
+                       }
+               } else if (!range.collapsed) {
+                       var cloneContents = range.cloneContents();
+                       if (!cloneContents) {
+                               cloneContents = this.document.createDocumentFragment();
+                       }
+                       html = this.editor.iframe.htmlRenderer.render(cloneContents, false);
+               }
+               return html;
+       },
+        /*
+        * Insert a node at the current position
+        * Delete the current selection, if any.
+        * Split the text node, if needed.
+        *
+        * @param       object          toBeInserted: the node to be inserted
+        *
+        * @return      object          this
+        */
+       insertNode: function (toBeInserted) {
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       this.insertHtml(toBeInserted.outerHTML);
+               } else {
+                       var range = this.createRange();
+                       range.deleteContents();
+                       toBeSelected = (toBeInserted.nodeType === HTMLArea.DOM.DOCUMENT_FRAGMENT_NODE) ? toBeInserted.lastChild : toBeInserted;
+                       range.insertNode(toBeInserted);
+                       this.selectNodeContents(toBeSelected, false);
+               }
+               return this;
+       },
+       /*
+        * Insert HTML source code at the current position
+        * Delete the current selection, if any.
+        *
+        * @param       string          html: the HTML source code
+        *
+        * @return      object          this
+        */
+       insertHtml: function (html) {
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       this.get();
+                       if (this.getType() === 'Control') {
+                               this.selection.clear();
+                               this.get();
+                       }
+                       var range = this.createRange();
+                       range.pasteHTML(html);
+               } else {
+                       this.editor.focus();
+                       var fragment = this.document.createDocumentFragment();
+                       var div = this.document.createElement('div');
+                       div.innerHTML = html;
+                       while (div.firstChild) {
+                               fragment.appendChild(div.firstChild);
+                       }
+                       this.insertNode(fragment);
+               }
+               return this;
+       },
+       /*
+        * Surround the selection with an element specified by its start and end tags
+        * Delete the selection, if any.
+        *
+        * @param       string          startTag: the start tag
+        * @param       string          endTag: the end tag
+        *
+        * @return      void
+        */
+       surroundHtml: function (startTag, endTag) {
+               this.insertHtml(startTag + this.getHtml().replace(HTMLArea.DOM.RE_bodyTag, '') + endTag);
+       },
+       /*
+        * Execute some native execCommand command on the current selection
+        *
+        * @param       string          cmdID: the command name or id
+        * @param       object          UI: 
+        * @param       object          param:
+        *
+        * @return      boolean         false
+        */
+       execCommand: function (cmdID, UI, param) {
+               this.editor.focus();
+               switch (cmdID) {
+                       default:
+                               try {
+                                       this.document.execCommand(cmdID, UI, param);
+                               } catch(e) {
+                                       this.editor.appendToLog('HTMLArea.DOM.Selection', 'execCommand', e + ' by execCommand(' + cmdID + ')', 'error');
+                               }
+               }
+               this.editor.updateToolbar();
+               return false;
+       },
+       /*
+        * Handle backspace event on the current selection
+        *
+        * @return      boolean         true to stop the event and cancel the default action
+        */
+       handleBackSpace: function () {
+               var range = this.createRange();
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       if (this.getType() === 'Control') {
+                                       // Deleting or backspacing on a control selection : delete the element
+                               var element = this.getParentElement();
+                               var parent = element.parentNode;
+                               parent.removeChild(el);
+                               return true;
+                       } else if (this.isEmpty()) {
+                                       // Check if deleting an empty block with a table as next sibling
+                               var element = this.getParentElement();
+                               if (!element.innerHTML && HTMLArea.DOM.isBlockElement(element) && element.nextSibling && /^table$/i.test(element.nextSibling.nodeName)) {
+                                       var previous = element.previousSibling;
+                                       if (!previous) {
+                                               this.selectNodeContents(element.nextSibling.rows[0].cells[0], true);
+                                       } else if (/^table$/i.test(previous.nodeName)) {
+                                               this.selectNodeContents(previous.rows[previous.rows.length-1].cells[previous.rows[previous.rows.length-1].cells.length-1], false);
+                                       } else {
+                                               range.moveStart('character', -1);
+                                               range.collapse(true);
+                                               range.select();
+                                       }
+                                       el.parentNode.removeChild(element);
+                                       return true;
+                               }
+                       } else {
+                                       // Backspacing into a link
+                               var range2 = range.duplicate();
+                               range2.moveStart('character', -1);
+                               var a = range2.parentElement();
+                               if (a != range.parentElement() && /^a$/i.test(a.nodeName)) {
+                                       range2.collapse(true);
+                                       range2.moveEnd('character', 1);
+                                       range2.pasteHTML('');
+                                       range2.select();
+                                       return true;
+                               }
+                               return false;
+                       }
+               } else {
+                       var self = this;
+                       window.setTimeout(function() {
+                               var range = self.createRange();
+                               var startContainer = range.startContainer;
+                               var startOffset = range.startOffset;
+                                       // If the selection is collapsed...
+                               if (self.isEmpty()) {
+                                               // ... and the cursor lies in a direct child of body...
+                                       if (/^(body)$/i.test(startContainer.nodeName)) {
+                                               var node = startContainer.childNodes[startOffset];
+                                       } else if (/^(body)$/i.test(startContainer.parentNode.nodeName)) {
+                                               var node = startContainer;
+                                       } else {
+                                               return false;
+                                       }
+                                               // ... which is a br or text node containing no non-whitespace character
+                                       if (/^(br|#text)$/i.test(node.nodeName) && !/\S/.test(node.textContent)) {
+                                                       // Get a meaningful previous sibling in which to reposition de cursor
+                                               var previousSibling = node.previousSibling;
+                                               while (previousSibling && /^(br|#text)$/i.test(previousSibling.nodeName) && !/\S/.test(previousSibling.textContent)) {
+                                                       previousSibling = previousSibling.previousSibling;
+                                               }
+                                                       // If there is no meaningful previous sibling, the cursor is at the start of body
+                                               if (previousSibling) {
+                                                               // Remove the node
+                                                       HTMLArea.DOM.removeFromParent(node);
+                                                               // Position the cursor
+                                                       if (/^(ol|ul|dl)$/i.test(previousSibling.nodeName)) {
+                                                               self.selectNodeContents(previousSibling.lastChild, false);
+                                                       } else if (/^(table)$/i.test(previousSibling.nodeName)) {
+                                                               self.selectNodeContents(previousSibling.rows[previousSibling.rows.length-1].cells[previousSibling.rows[previousSibling.rows.length-1].cells.length-1], false);
+                                                       } else if (!/\S/.test(previousSibling.textContent) && previousSibling.firstChild) {
+                                                               self.selectNode(previousSibling.firstChild, true);
+                                                       } else {
+                                                               self.selectNodeContents(previousSibling, false);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }, 10);
+                       return false;   
+               }
+       },
+       /*
+        * Detect emails and urls as they are typed in non-IE browsers
+        * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
+        *
+        * @param       object          event: the ExtJS key event 
+        *
+        * @return      void
+        */
+       detectURL: function (event) {
+               var ev = event.browserEvent;
+               var editor = this.editor;
+               var selection = this.get().selection;
+               if (!/^(a)$/i.test(this.getParentElement().nodeName)) {
+                       var autoWrap = function (textNode, tag) {
+                               var rightText = textNode.nextSibling;
+                               if (typeof(tag) === 'string') {
+                                       tag = editor.document.createElement(tag);
+                               }
+                               var a = textNode.parentNode.insertBefore(tag, rightText);
+                               HTMLArea.DOM.removeFromParent(textNode);
+                               a.appendChild(textNode);
+                               selection.collapse(rightText, 0);
+                               rightText.parentNode.normalize();
+               
+                               editor.unLink = function() {
+                                       var t = a.firstChild;
+                                       a.removeChild(t);
+                                       a.parentNode.insertBefore(t, a);
+                                       HTMLArea.DOM.removeFromParent(a);
+                                       t.parentNode.normalize();
+                                       editor.unLink = null;
+                                       editor.unlinkOnUndo = false;
+                               };
+               
+                               editor.unlinkOnUndo = true;
+                               return a;
+                       };
+                       switch (ev.which) {
+                                       // Space or Enter or >, see if the text just typed looks like a URL, or email address and link it accordingly
+                               case 13:
+                               case 32:
+                                       if (selection && selection.isCollapsed && selection.anchorNode.nodeType === HTMLArea.DOM.TEXT_NODE && selection.anchorNode.data.length > 3 && selection.anchorNode.data.indexOf('.') >= 0) {
+                                               var midStart = selection.anchorNode.data.substring(0,selection.anchorOffset).search(/[a-zA-Z0-9]+\S{3,}$/);
+                                               if (midStart == -1) {
+                                                       break;
+                                               }
+                                               if (this.getFirstAncestorOfType('a')) {
+                                                               // already in an anchor
+                                                       break;
+                                               }
+                                               var matchData = selection.anchorNode.data.substring(0,selection.anchorOffset).replace(/^.*?(\S*)$/, '$1');
+                                               if (matchData.indexOf('@') != -1) {
+                                                       var m = matchData.match(HTMLArea.RE_email);
+                                                       if (m) {
+                                                               var leftText  = selection.anchorNode;
+                                                               var rightText = leftText.splitText(selection.anchorOffset);
+                                                               var midText   = leftText.splitText(midStart);
+                                                               var midEnd = midText.data.search(/[^a-zA-Z0-9\.@_\-]/);
+                                                               if (midEnd != -1) {
+                                                                       var endText = midText.splitText(midEnd);
+                                                               }
+                                                               autoWrap(midText, 'a').href = 'mailto:' + m[0];
+                                                               break;
+                                                       }
+                                               }
+                                               var m = matchData.match(HTMLArea.RE_url);
+                                               if (m) {
+                                                       var leftText  = selection.anchorNode;
+                                                       var rightText = leftText.splitText(selection.anchorOffset);
+                                                       var midText   = leftText.splitText(midStart);
+                                                       var midEnd = midText.data.search(/[^a-zA-Z0-9\._\-\/\&\?=:@]/);
+                                                       if (midEnd != -1) {
+                                                               var endText = midText.splitText(midEnd);
+                                                       }
+                                                       autoWrap(midText, 'a').href = (m[1] ? m[1] : 'http://') + m[3];
+                                                       break;
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       if (ev.keyCode == 27 || (editor.unlinkOnUndo && ev.ctrlKey && ev.which == 122)) {
+                                               if (editor.unLink) {
+                                                       editor.unLink();
+                                                       event.stopEvent();
+                                               }
+                                               break;
+                                       } else if (ev.which || ev.keyCode == 8 || ev.keyCode == 46) {
+                                               editor.unlinkOnUndo = false;
+                                               if (selection.anchorNode && selection.anchorNode.nodeType === HTMLArea.DOM.TEXT_NODE) {
+                                                               // See if we might be changing a link
+                                                       var a = this.getFirstAncestorOfType('a');
+                                                       if (!a) {
+                                                               break;
+                                                       }
+                                                       if (!a.updateAnchorTimeout) {
+                                                               if (selection.anchorNode.data.match(HTMLArea.RE_email) && (a.href.match('mailto:' + selection.anchorNode.data.trim()))) {
+                                                                       var textNode = selection.anchorNode;
+                                                                       var fn = function() {
+                                                                               a.href = 'mailto:' + textNode.data.trim();
+                                                                               a.updateAnchorTimeout = setTimeout(fn, 250);
+                                                                       };
+                                                                       a.updateAnchorTimeout = setTimeout(fn, 250);
+                                                                       break;
+                                                               }
+                                                               var m = selection.anchorNode.data.match(HTMLArea.RE_url);
+                                                               if (m &&  a.href.match(selection.anchorNode.data.trim())) {
+                                                                       var textNode = selection.anchorNode;
+                                                                       var fn = function() {
+                                                                               var m = textNode.data.match(HTMLArea.RE_url);
+                                                                               a.href = (m[1] ? m[1] : 'http://') + m[3];
+                                                                               a.updateAnchorTimeout = setTimeout(fn, 250);
+                                                                       }
+                                                                       a.updateAnchorTimeout = setTimeout(fn, 250);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       break;
+                       }
+               }
+       },
+       /*
+        * Enter event handler
+        *
+        * @return      boolean         true to stop the event and cancel the default action
+        */
+       checkInsertParagraph: function() {
+               var editor = this.editor;
+               var i, left, right, rangeClone,
+                       sel     = this.get().selection,
+                       range   = this.createRange(),
+                       p       = this.getAllAncestors(),
+                       block   = null,
+                       a       = null,
+                       doc     = this.document;
+               for (i = 0; i < p.length; ++i) {
+                       if (HTMLArea.DOM.isBlockElement(p[i]) && !/^(html|body|table|tbody|thead|tfoot|tr|dl)$/i.test(p[i].nodeName)) {
+                               block = p[i];
+                               break;
+                       }
+               }
+               if (block && /^(td|th|tr|tbody|thead|tfoot|table)$/i.test(block.nodeName) && this.editor.config.buttons.table && this.editor.config.buttons.table.disableEnterParagraphs) {
+                       return false;
+               }
+               if (!range.collapsed) {
+                       range.deleteContents();
+               }
+               this.empty();
+               if (!block || /^(td|div)$/i.test(block.nodeName)) {
+                       if (!block) {
+                               block = doc.body;
+                       }
+                       if (block.hasChildNodes()) {
+                               rangeClone = range.cloneRange();
+                               if (range.startContainer == block) {
+                                               // Selection is directly under the block
+                                       var blockOnLeft = null;
+                                       var leftSibling = null;
+                                               // Looking for the farthest node on the left that is not a block
+                                       for (var i = range.startOffset; --i >= 0;) {
+                                               if (HTMLArea.DOM.isBlockElement(block.childNodes[i])) {
+                                                       blockOnLeft = block.childNodes[i];
+                                                       break;
+                                               } else {
+                                                       rangeClone.setStartBefore(block.childNodes[i]);
+                                               }
+                                       }
+                               } else {
+                                               // Looking for inline or text container immediate child of block
+                                       var inlineContainer = range.startContainer;
+                                       while (inlineContainer.parentNode != block) {
+                                               inlineContainer = inlineContainer.parentNode;
+                                       }
+                                               // Looking for the farthest node on the left that is not a block
+                                       var leftSibling = inlineContainer;
+                                       while (leftSibling.previousSibling && !HTMLArea.DOM.isBlockElement(leftSibling.previousSibling)) {
+                                               leftSibling = leftSibling.previousSibling;
+                                       }
+                                       rangeClone.setStartBefore(leftSibling);
+                                       var blockOnLeft = leftSibling.previousSibling;
+                               }
+                                       // Avoiding surroundContents buggy in Opera and Safari
+                               left = doc.createElement('p');
+                               left.appendChild(rangeClone.extractContents());
+                               if (!left.textContent && !left.getElementsByTagName('img').length && !left.getElementsByTagName('table').length) {
+                                       left.innerHTML = '<br />';
+                               }
+                               if (block.hasChildNodes()) {
+                                       if (blockOnLeft) {
+                                               left = block.insertBefore(left, blockOnLeft.nextSibling);
+                                       } else {
+                                               left = block.insertBefore(left, block.firstChild);
+                                       }
+                               } else {
+                                       left = block.appendChild(left);
+                               }
+                               block.normalize();
+                                       // Looking for the farthest node on the right that is not a block
+                               var rightSibling = left;
+                               while (rightSibling.nextSibling && !HTMLArea.DOM.isBlockElement(rightSibling.nextSibling)) {
+                                       rightSibling = rightSibling.nextSibling;
+                               }
+                               var blockOnRight = rightSibling.nextSibling;
+                               range.setEndAfter(rightSibling);
+                               range.setStartAfter(left);
+                                       // Avoiding surroundContents buggy in Opera and Safari
+                               right = doc.createElement('p');
+                               right.appendChild(range.extractContents());
+                               if (!right.textContent && !right.getElementsByTagName('img').length && !right.getElementsByTagName('table').length) {
+                                       right.innerHTML = '<br />';
+                               }
+                               if (!(left.childNodes.length == 1 && right.childNodes.length == 1 && left.firstChild.nodeName.toLowerCase() == 'br' && right.firstChild.nodeName.toLowerCase() == 'br')) {
+                                       if (blockOnRight) {
+                                               right = block.insertBefore(right, blockOnRight);
+                                       } else {
+                                               right = block.appendChild(right);
+                                       }
+                                       this.selectNodeContents(right, true);
+                               } else {
+                                       this.selectNodeContents(left, true);
+                               }
+                               block.normalize();
+                       } else {
+                               var first = block.firstChild;
+                               if (first) {
+                                       block.removeChild(first);
+                               }
+                               right = doc.createElement('p');
+                               if (Ext.isWebKit || Ext.isOpera) {
+                                       right.innerHTML = '<br />';
+                               }
+                               right = block.appendChild(right);
+                               this.selectNodeContents(right, true);
+                       }
+               } else {
+                       range.setEndAfter(block);
+                       var df = range.extractContents(), left_empty = false;
+                       if (!/\S/.test(block.innerHTML) || (!/\S/.test(block.textContent) && !/<(img|hr|table)/i.test(block.innerHTML))) {
+                               if (!Ext.isOpera) {
+                                       block.innerHTML = '<br />';
+                               }
+                               left_empty = true;
+                       }
+                       p = df.firstChild;
+                       if (p) {
+                               if (!/\S/.test(p.innerHTML) || (!/\S/.test(p.textContent) && !/<(img|hr|table)/i.test(p.innerHTML))) {
+                                       if (/^h[1-6]$/i.test(p.nodeName)) {
+                                               p = HTMLArea.DOM.convertNode(p, 'p');
+                                       }
+                                       if (/^(dt|dd)$/i.test(p.nodeName)) {
+                                                p = HTMLArea.DOM.convertNode(p, /^(dt)$/i.test(p.nodeName) ? 'dd' : 'dt');
+                                       }
+                                       if (!Ext.isOpera) {
+                                               p.innerHTML = '<br />';
+                                       }
+                                       if (/^li$/i.test(p.nodeName) && left_empty && (!block.nextSibling || !/^li$/i.test(block.nextSibling.nodeName))) {
+                                               left = block.parentNode;
+                                               left.removeChild(block);
+                                               range.setEndAfter(left);
+                                               range.collapse(false);
+                                               p = HTMLArea.DOM.convertNode(p, /^(li|dd|td|th|p|h[1-6])$/i.test(left.parentNode.nodeName) ? 'br' : 'p');
+                                       }
+                               }
+                               range.insertNode(df);
+                                       // Remove any anchor created empty on both sides of the selection
+                               if (p.previousSibling) {
+                                       var a = p.previousSibling.lastChild;
+                                       if (a && /^a$/i.test(a.nodeName) && !/\S/.test(a.innerHTML)) {
+                                               HTMLArea.DOM.convertNode(a, 'br');
+                                       }
+                               }
+                               var a = p.lastChild;
+                               if (a && /^a$/i.test(a.nodeName) && !/\S/.test(a.innerHTML)) {
+                                       HTMLArea.DOM.convertNode(a, 'br');
+                               }
+                                       // Walk inside the deepest child element (presumably inline element)
+                               while (p.firstChild && p.firstChild.nodeType === HTMLArea.DOM.ELEMENT_NODE && !/^(br|img|hr|table)$/i.test(p.firstChild.nodeName)) {
+                                       p = p.firstChild;
+                               }
+                               if (/^br$/i.test(p.nodeName)) {
+                                       p = p.parentNode.insertBefore(doc.createTextNode('\x20'), p);
+                               } else if (!/\S/.test(p.innerHTML)) {
+                                               // Need some element inside the deepest element
+                                       p.appendChild(doc.createElement('br'));
+                               }
+                               this.selectNodeContents(p, true);
+                       } else {
+                               if (/^(li|dt|dd)$/i.test(block.nodeName)) {
+                                       p = doc.createElement(block.nodeName);
+                               } else {
+                                       p = doc.createElement('p');
+                               }
+                               if (!Ext.isOpera) {
+                                       p.innerHTML = '<br />';
+                               }
+                               if (block.nextSibling) {
+                                       p = block.parentNode.insertBefore(p, block.nextSibling);
+                               } else {
+                                       p = block.parentNode.appendChild(p);
+                               }
+                               this.selectNodeContents(p, true);
+                       }
+               }
+               this.editor.scrollToCaret();
+               return true;
+       }
+});
+/***************************************************
+ *  HTMLArea.DOM.BookMark: BookMark object
+ ***************************************************/
+HTMLArea.DOM.BookMark = function (config) {
+};
+HTMLArea.DOM.BookMark = Ext.extend(HTMLArea.DOM.BookMark, {
+       /*
+        * Reference to the editor MUST be set in config
+        */
+       editor: null,
+       /*
+        * Reference to the editor document
+        */
+       document: null,
+       /*
+        * Reference to the editor selection object
+        */
+       selection: null,
+       /*
+        * HTMLArea.DOM.Selection constructor
+        */
+       constructor: function (config) {
+                       // Apply config
+               Ext.apply(this, config);
+                       // Initialize references
+               this.document = this.editor.document;
+               this.selection = this.editor.getSelection();
+       },
+       /*
+        * Get a bookMark
+        * Adapted from FCKeditor
+        * This is an "intrusive" way to create a bookMark. It includes <span> tags
+        * in the range boundaries. The advantage of it is that it is possible to
+        * handle DOM mutations when moving back to the bookMark.
+        *
+        * @param       object          range: the range to bookMark
+        *
+        * @return      object          the bookMark
+        */
+       get: function (range) {
+               var bookMark;
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                               // Bookmarking will not work on control ranges
+                       try {
+                               bookMark = range.getBookmark();
+                       } catch (e) {
+                               bookMark = null;
+                       }
+               } else {
+                               // Create the bookmark info (random IDs).
+                       var bookMark = {
+                               startId : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'S',
+                               endId   : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'E'
+                       };
+                       var startSpan;
+                       var endSpan;
+                       var rangeClone = range.cloneRange();
+                               // For collapsed ranges, add just the start marker
+                       if (!range.collapsed ) {
+                               endSpan = this.document.createElement('span');
+                               endSpan.style.display = 'none';
+                               endSpan.id = bookMark.endId;
+                               endSpan.setAttribute('data-htmlarea-bookmark', true);
+                               endSpan.innerHTML = '&nbsp;';
+                               rangeClone.collapse(false);
+                               rangeClone.insertNode(endSpan);
+                       }
+                       startSpan = this.document.createElement('span');
+                       startSpan.style.display = 'none';
+                       startSpan.id = bookMark.startId;
+                       startSpan.setAttribute('data-htmlarea-bookmark', true);
+                       startSpan.innerHTML = '&nbsp;';
+                       var rangeClone = range.cloneRange();
+                       rangeClone.collapse(true);
+                       rangeClone.insertNode(startSpan);
+                       bookMark.startNode = startSpan;
+                       bookMark.endNode = endSpan;
+                               // Update the range position.
+                       if (endSpan) {
+                               range.setEndBefore(endSpan);
+                               range.setStartAfter(startSpan);
+                       } else {
+                               range.setEndAfter(startSpan);
+                               range.collapse(false);
+                       }
+                       return bookMark;
+               }
+       },
+       /*
+        * Get the end point of the bookMark
+        * Adapted from FCKeditor
+        *
+        * @param       object          bookMark: the bookMark
+        * @param       boolean         endPoint: true, for startPoint, false for endPoint
+        *
+        * @return      object          the endPoint node
+        */
+       getEndPoint: function (bookMark, endPoint) {
+               if (endPoint) {
+                       return this.document.getElementById(bookMark.startId);
+               } else {
+                       return this.document.getElementById(bookMark.endId);
+               }
+       },
+       /*
+        * Move the range to the bookmark
+        * Adapted from FCKeditor
+        *
+        * @param       object          bookMark: the bookmark to move to
+        *
+        * @return      object          the range that was bookmarked
+        */
+       moveTo: function (bookMark) {
+               var range = this.selection.createRange();
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       if (bookMark) {
+                               range.moveToBookmark(bookMark);
+                       }
+               } else {
+                       var startSpan  = this.getEndPoint(bookMark, true);
+                       var endSpan    = this.getEndPoint(bookMark, false);
+                       var parent;
+                       if (startSpan) {
+                                       // If the previous sibling is a text node, let the anchorNode have it as parent
+                               if (startSpan.previousSibling && startSpan.previousSibling.nodeType === HTMLArea.DOM.TEXT_NODE) {
+                                       range.setStart(startSpan.previousSibling, startSpan.previousSibling.data.length);
+                               } else {
+                                       range.setStartBefore(startSpan);
+                               }
+                               HTMLArea.DOM.removeFromParent(startSpan);
+                       } else {
+                                       // For some reason, the startSpan was removed or its id attribute was removed so that it cannot be retrieved
+                               range.setStart(this.document.body, 0);
+                       }
+                               // If the bookmarked range was collapsed, the end span will not be available
+                       if (endSpan) {
+                                       // If the next sibling is a text node, let the focusNode have it as parent
+                               if (endSpan.nextSibling && endSpan.nextSibling.nodeType === HTMLArea.DOM.TEXT_NODE) {
+                                       range.setEnd(endSpan.nextSibling, 0);
+                               } else {
+                                       range.setEndBefore(endSpan);
+                               }
+                               HTMLArea.DOM.removeFromParent(endSpan);
+                       } else {
+                               range.collapse(true);
+                       }
+               }
+               return range;
+       }
+});
+/***************************************************
+ *  HTMLArea.DOM.Node: Node object
+ ***************************************************/
+HTMLArea.DOM.Node = function (config) {
+};
+HTMLArea.DOM.Node = Ext.extend(HTMLArea.DOM.Node, {
+       /*
+        * Reference to the editor MUST be set in config
+        */
+       editor: null,
+       /*
+        * Reference to the editor document
+        */
+       document: null,
+       /*
+        * Reference to the editor selection object
+        */
+       selection: null,
+       /*
+        * Reference to the editor bookmark object
+        */
+       bookMark: null,
+       /*
+        * HTMLArea.DOM.Selection constructor
+        */
+       constructor: function (config) {
+                       // Apply config
+               Ext.apply(this, config);
+                       // Initialize references
+               this.document = this.editor.document;
+               this.selection = this.editor.getSelection();
+               this.bookMark = this.editor.getBookMark();
+       },
+       /*
+        * Remove the given element
+        *
+        * @param       object          element: the element to be removed, content and selection being preserved
+        *
+        * @return      void
+        */
+       removeMarkup: function (element) {
+               var bookMark = this.bookMark.get(this.selection.createRange());
+               var parent = element.parentNode;
+               while (element.firstChild) {
+                       parent.insertBefore(element.firstChild, element);
+               }
+               parent.removeChild(element);
+               this.selection.selectRange(this.bookMark.moveTo(bookMark));
+       },
+       /*
+        * Wrap the range with an inline element
+        *
+        * @param       string  element: the node that will wrap the range
+        * @param       object  range: the range to be wrapped
+        *
+        * @return      void
+        */
+       wrapWithInlineElement: function (element, range) {
+               if (Ext.isIE8 || Ext.isIE7 || Ext.isIE6) {
+                       var nodeName = element.nodeName;
+                       var bookMark = this.bookMark.get(range);
+                       if (range.parentElement) {
+                               var parent = range.parentElement();
+                               var rangeStart = range.duplicate();
+                               rangeStart.collapse(true);
+                               var parentStart = rangeStart.parentElement();
+                               var rangeEnd = range.duplicate();
+                               rangeEnd.collapse(true);
+                               var newRange = this.selection.createRange();
+                               
+                               var parentEnd = rangeEnd.parentElement();
+                               var upperParentStart = parentStart;
+                               if (parentStart !== parent) {
+                                       while (upperParentStart.parentNode !== parent) {
+                                               upperParentStart = upperParentStart.parentNode;
+                                       }
+                               }
+                               
+                               element.innerHTML = range.htmlText;
+                                       // IE eats spaces on the start boundary
+                               if (range.htmlText.charAt(0) === '\x20') {
+                                       element.innerHTML = '&nbsp;' + element.innerHTML;
+                               }
+                               var elementClone = element.cloneNode(true);
+                               range.pasteHTML(element.outerHTML);
+                                       // IE inserts the element as the last child of the start container
+                               if (parentStart !== parent
+                                               && parentStart.lastChild
+                                               && parentStart.lastChild.nodeType === HTMLArea.DOM.ELEMENT_NODE
+                                               && parentStart.lastChild.nodeName.toLowerCase() === nodeName) {
+                                       parent.insertBefore(elementClone, upperParentStart.nextSibling);
+                                       parentStart.removeChild(parentStart.lastChild);
+                                               // Sometimes an empty previous sibling was created
+                                       if (elementClone.previousSibling
+                                                       && elementClone.previousSibling.nodeType === HTMLArea.DOM.ELEMENT_NODE
+                                                       && !elementClone.previousSibling.innerText) {
+                                               parent.removeChild(elementClone.previousSibling);
+                                       }
+                                               // The bookmark will not work anymore
+                                       newRange.moveToElementText(elementClone);
+                                       newRange.collapse(false);
+                                       newRange.select();
+                               } else {
+                                               // Working around IE boookmark bug
+                                       if (parentStart != parentEnd) {
+                                               var newRange = this.selection.createRange();
+                                               if (newRange.moveToBookmark(bookMark)) {
+                                                       newRange.collapse(false);
+                                                       newRange.select();
+                                               }
+                                       } else {
+                                               range.collapse(false);
+                                       }
+                               }
+                               parent.normalize();
+                       } else {
+                               var parent = range.item(0);
+                               element = parent.parentNode.insertBefore(element, parent);
+                               element.appendChild(parent);
+                               this.bookMark.moveTo(bookMark);
+                       }
+               } else {
+                       element.appendChild(range.extractContents());
+                       range.insertNode(element);
+                       element.normalize();
+                               // Sometimes Firefox inserts empty elements just outside the boundaries of the range
+                       var neighbour = element.previousSibling;
+                       if (neighbour && (neighbour.nodeType !== HTMLArea.DOM.TEXT_NODE) && !/\S/.test(neighbour.textContent)) {
+                               HTMLArea.DOM.removeFromParent(neighbour);
+                       }
+                       neighbour = element.nextSibling;
+                       if (neighbour && (neighbour.nodeType !== HTMLArea.DOM.TEXT_NODE) && !/\S/.test(neighbour.textContent)) {
+                               HTMLArea.DOM.removeFromParent(neighbour);
+                       }
+                       this.selection.selectNodeContents(element, false);
+               }
+       },
+       /*
+        * Clean Apple wrapping span and font elements under the specified node
+        *
+        * @param       object          node: the node in the subtree of which cleaning is performed
+        *
+        * @return      void
+        */
+       cleanAppleStyleSpans: function (node) {
+               if (Ext.isWebKit) {
+                       if (node.getElementsByClassName) {
+                               var spans = node.getElementsByClassName('Apple-style-span');
+                               for (var i = spans.length; --i >= 0;) {
+                                       this.removeMarkup(spans[i]);
+                               }
+                       } else {
+                               var spans = node.getElementsByTagName('span');
+                               for (var i = spans.length; --i >= 0;) {
+                                       if (HTMLArea.DOM.hasClass(spans[i], 'Apple-style-span')) {
+                                               this.removeMarkup(spans[i]);
+                                       }
+                               }
+                               var fonts = node.getElementsByTagName('font');
+                               for (i = fonts.length; --i >= 0;) {
+                                       if (HTMLArea.DOM.hasClass(fonts[i], 'Apple-style-span')) {
+                                               this.removeMarkup(fonts[i]);
+                                       }
+                               }
+                       }
+               }
+       }
+});
+ /*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.getInnerText = HTMLArea.DOM.getInnerText;
+ /*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.removeFromParent = HTMLArea.DOM.removeFromParent;
+/*
+ * Get the block ancestors of an element within a given block
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getBlockAncestors = HTMLArea.DOM.getBlockAncestors;
+/*
+ * This function verifies if the element has any allowed attributes
+ *
+ * @param      object  element: the DOM element
+ * @param      array   allowedAttributes: array of allowed attribute names
+ *
+ * @return     boolean true if the element has one of the allowed attributes
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.hasAllowedAttributes = HTMLArea.DOM.hasAllowedAttributes;
+/*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.isBlockElement = HTMLArea.DOM.isBlockElement;
+/*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.needsClosingTag = HTMLArea.DOM.needsClosingTag;
+/*
+ * Get the current selection object
+ * 
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype._getSelection = function() {
+       this.appendToLog('HTMLArea.Editor', '_getSelection', 'Reference to deprecated method', 'warn');
+       return this.getSelection().get().selection;
+};
+/*
+ * Empty the selection object
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.emptySelection = function (selection) {
+       this.appendToLog('HTMLArea.Editor', 'emptySelection', 'Reference to deprecated method', 'warn');
+       this.getSelection().empty();
+};
+/*
+ * Add a range to the selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.addRangeToSelection = function(selection, range) {
+       this.appendToLog('HTMLArea.Editor', 'addRangeToSelection', 'Reference to deprecated method', 'warn');
+       this.getSelection().addRange(range);
+};
+/*
+ * Create a range for the current selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype._createRange = function(sel) {
+       this.appendToLog('HTMLArea.Editor', '_createRange', 'Reference to deprecated method', 'warn');
+       return this.getSelection().createRange();
+};
+/*
+ * Select a node AND the contents inside the node
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.selectNode = function(node, endPoint) {
+       this.appendToLog('HTMLArea.Editor', 'selectNode', 'Reference to deprecated method', 'warn');
+       this.getSelection().selectNode(node, endPoint);
+};
+/*
+ * Select ONLY the contents inside the given node
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.selectNodeContents = function(node, endPoint) {
+       this.appendToLog('HTMLArea.Editor', 'selectNodeContents', 'Reference to deprecated method', 'warn');
+       this.getSelection().selectNodeContents(node, endPoint);
+};
+/*
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.rangeIntersectsNode = function(range, node) {
+       this.appendToLog('HTMLArea.Editor', 'rangeIntersectsNode', 'Reference to deprecated method', 'warn');
+       this.focus();
+       return HTMLArea.DOM.rangeIntersectsNode(range, node);
+};
+/*
+ * Get the selection type
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getSelectionType = function(selection) {
+       this.appendToLog('HTMLArea.Editor', 'getSelectionType', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getType();
+};
+/*
+ * Return the ranges of the selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getSelectionRanges = function(selection) {
+       this.appendToLog('HTMLArea.Editor', 'getSelectionRanges', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getRanges();
+};
+/*
+ * Add ranges to the selection
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.setSelectionRanges = function(ranges, selection) {
+       this.appendToLog('HTMLArea.Editor', 'setSelectionRanges', 'Reference to deprecated method', 'warn');
+       this.getSelection().setRanges(ranges);
+};
+/*
+ * Retrieves the selected element (if any), just in the case that a single element (object like and image or a table) is selected.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getSelectedElement = function(selection) {
+       this.appendToLog('HTMLArea.Editor', 'getSelectedElement', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getElement();
+};
+/*
+ * Retrieve the HTML contents of selected block
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getSelectedHTML = function() {
+       this.appendToLog('HTMLArea.Editor', 'getSelectedHTML', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getHtml();
+};
+/*
+ * Retrieve simply HTML contents of the selected block, IE ignoring control ranges
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getSelectedHTMLContents = function() {
+       this.appendToLog('HTMLArea.Editor', 'getSelectedHTMLContents', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getHtml();
+};
+/*
+ * Get the deepest node that contains both endpoints of the current selection.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getParentElement = function(selection, range) {
+       this.appendToLog('HTMLArea.Editor', 'getParentElement', 'Reference to deprecated method', 'warn');
+       return this.getSelection().getParentElement();
+};
+/*
+ * Determine if the current selection is empty or not.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype._selectionEmpty = function(sel) {
+       this.appendToLog('HTMLArea.Editor', '_selectionEmpty', 'Reference to deprecated method', 'warn');
+       return this.getSelection().isEmpty();
+};
+/*
+ * Get a bookmark
+ * Adapted from FCKeditor
+ * This is an "intrusive" way to create a bookmark. It includes <span> tags
+ * in the range boundaries. The advantage of it is that it is possible to
+ * handle DOM mutations when moving back to the bookmark.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getBookmark = function (range) {
+       this.appendToLog('HTMLArea.Editor', 'getBookmark', 'Reference to deprecated method', 'warn');
+       return this.getBookMark().get(range);
+};
+/*
+ * Get the end point of the bookmark
+ * Adapted from FCKeditor
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.getBookmarkNode = function(bookmark, endPoint) {
+       this.appendToLog('HTMLArea.Editor', 'getBookmarkNode', 'Reference to deprecated method', 'warn');
+       return this.getBookMark().getEndPoint(bookmark, endPoint);
+};
+/*
+ * Move the range to the bookmark
+ * Adapted from FCKeditor
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.moveToBookmark = function (bookmark) {
+       this.appendToLog('HTMLArea.Editor', 'moveToBookmark', 'Reference to deprecated method', 'warn');
+       return this.getBookMark().moveTo(bookmark);
+};
+/*
+ * Select range
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.selectRange = function (range) {
+       this.appendToLog('HTMLArea.Editor', 'selectRange', 'Reference to deprecated method', 'warn');
+       this.selection.selectRange(range);
+};
+ /*
+ * Insert a node at the current position.
+ * Delete the current selection, if any.
+ * Split the text node, if needed.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.insertNodeAtSelection = function(toBeInserted) {
+       this.appendToLog('HTMLArea.Editor', 'insertNodeAtSelection', 'Reference to deprecated method', 'warn');
+       this.getSelection().insertNode(toBeInserted);
+};
+/*
+ * Insert HTML source code at the current position.
+ * Delete the current selection, if any.
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.insertHTML = function(html) {
+       this.appendToLog('HTMLArea.Editor', 'insertHTML', 'Reference to deprecated method', 'warn');
+       this.getSelection().insertHtml(html);
+};
+/*
+ * Wrap the range with an inline element
+ *
+ * @param      string  element: the node that will wrap the range
+ * @param      object  range: the range to be wrapped
+ *
+ * @return     void
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.wrapWithInlineElement = function(element, selection,range) {
+       this.appendToLog('HTMLArea.Editor', 'wrapWithInlineElement', 'Reference to deprecated method', 'warn');
+       this.getDomNode().wrapWithInlineElement(element, range);
+};
+/*
+ * Clean Apple wrapping span and font elements under the specified node
+ *
+ * @param      object          node: the node in the subtree of which cleaning is performed
+ *
+ * @return     void
+ *
+ ***********************************************
+ * THIS FUNCTION IS DEPRECATED AS OF TYPO3 4.7 *
+ ***********************************************
+ */
+HTMLArea.Editor.prototype.cleanAppleStyleSpans = function(node) {
+       this.appendToLog('HTMLArea.Editor', 'cleanAppleStyleSpans', 'Reference to deprecated method', 'warn');
+       this.getDomNode().cleanAppleStyleSpans(node);
+};
+/***************************************************
+ *  HTMLArea.CSS.Parser: CSS Parser
  ***************************************************/
 HTMLArea.CSS.Parser = Ext.extend(Ext.util.Observable, {
        /*
@@ -3778,12 +5567,12 @@ HTMLArea.util.Color = function () {
                                        var b = parseInt(RegExp.$3);
                                        return ('#' + hex(r) + hex(g) + hex(b)).toUpperCase();
                                }
-                               return null;
+                               return '';
                        }
                        if (v.substr(0, 1) === '#') {
                                return v;
                        }
-                       return null;
+                       return '';
                },
                /*
                 * Select interceptor to ensure that the color exists in the palette before trying to select
@@ -4003,52 +5792,17 @@ Ext.ux.form.ColorPaletteField = Ext.extend(Ext.form.TriggerField, {
        }
 });
 Ext.reg('colorpalettefield', Ext.ux.form.ColorPaletteField);
-/**
- * Internet Explorer returns an item having the _name_ equal to the given id, even if it's not having any id.
- * This way it can return a different form field even if it's not a textarea.  This works around the problem by
- * specifically looking to search only elements having a certain tag name.
- */
-HTMLArea.getElementById = function(tag, id) {
-       var el, i, objs = document.getElementsByTagName(tag);
-       for (i = objs.length; --i >= 0 && (el = objs[i]);) {
-               if (el.id == id) return el;
-       }
-       return null;
-};
-
 /***************************************************
  * TYPO3-SPECIFIC FUNCTIONS
  ***************************************************/
 /*
  * Extending the TYPO3 Lorem Ipsum extension
  */
-var lorem_ipsum = function(element,text) {
-       if (element.tagName.toLowerCase() == "textarea" && element.id && element.id.substr(0,7) == "RTEarea") {
-               var editor = RTEarea[element.id.substr(7, element.id.length)]["editor"];
-               editor.insertHTML(text);
-               editor.toolbar.update();
-       }
-};
-/*
- * Create the editor when HTMLArea is loaded and when Ext is ready
- */
-HTMLArea.initEditor = function(editorNumber) {
-       if (document.getElementById('pleasewait' + editorNumber)) {
-               if (HTMLArea.checkSupportedBrowser()) {
-                       document.getElementById('pleasewait' + editorNumber).style.display = 'block';
-                       document.getElementById('editorWrap' + editorNumber).style.visibility = 'hidden';
-                       if (!HTMLArea.isReady) {
-                               HTMLArea.initEditor.defer(150, null, [editorNumber]);
-                       } else {
-                                       // Create an editor for the textarea
-                               var editor = new HTMLArea.Editor(Ext.apply(new HTMLArea.Config(editorNumber), RTEarea[editorNumber]));
-                               editor.generate();
-                               return false;
-                       }
-               } else {
-                       document.getElementById('pleasewait' + editorNumber).style.display = 'none';
-                       document.getElementById('editorWrap' + editorNumber).style.visibility = 'visible';
-               }
+var lorem_ipsum = function (element, text) {
+       if (/^textarea$/i.test(element.nodeName) && element.id && element.id.substr(0,7) === 'RTEarea') {
+               var editor = RTEarea[element.id.substr(7, element.id.length)]['editor'];
+               editor.getSelection().insertHtml(text);
+               editor.updateToolbar();
        }
 };
 /**
@@ -4147,7 +5901,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      boolean         true if the information was registered
         */
-       registerPluginInformation: function(pluginInformation) {
+       registerPluginInformation: function (pluginInformation) {
                if (typeof(pluginInformation) !== 'object') {
                        this.appendToLog('registerPluginInformation', 'Plugin information was not provided', 'warn');
                        return false;
@@ -4163,7 +5917,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      object          the plugin information object
         */
-       getPluginInformation : function() {
+       getPluginInformation: function () {
                return this.pluginInformation;
        },
 
@@ -4173,7 +5927,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         * @param       string          pluinName: the name of some plugin
         * @return      object          the plugin object or null
         */
-       getPluginInstance : function(pluginName) {
+       getPluginInstance: function (pluginName) {
                return this.editor.getPlugin(pluginName);
        },
 
@@ -4182,7 +5936,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      string          editor mode
         */
-       getEditorMode : function() {
+       getEditorMode: function () {
                return this.editor.getMode();
        },
 
@@ -4193,7 +5947,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      boolean         true if the button is enabled in the toolbar configuration
         */
-       isButtonInToolbar : function(buttonId) {
+       isButtonInToolbar: function (buttonId) {
                var index = -1;
                Ext.each(this.editorConfiguration.toolbar, function (row) {
                        Ext.each(row, function (group) {
@@ -4212,7 +5966,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      object          the toolbar button object
         */
-       getButton: function(buttonId) {
+       getButton: function (buttonId) {
                return this.editor.toolbar.getButton(buttonId);
        },
        /**
@@ -4651,7 +6405,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
        saveSelection: function () {
                        // If IE, save the current selection
                if (Ext.isIE) {
-                       this.savedRange = this.editor._createRange(this.editor._getSelection());
+                       this.savedRange = this.editor.getSelection().createRange();
                }
        },
        /*
@@ -4663,7 +6417,7 @@ HTMLArea.Plugin = Ext.extend(HTMLArea.Plugin, {
                if (Ext.isIE && this.savedRange) {
                                // Restoring the selection will not work if the inner html was replaced by the plugin
                        try {
-                               this.editor.selectRange(this.savedRange);
+                               this.editor.getSelection().selectRange(this.savedRange);
                        } catch (e) {}
                }
        },
index 1f27517..cf98b3a 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2005-2011 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  (c) 2005-2012 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -37,7 +37,7 @@ HTMLArea.Acronym = Ext.extend(HTMLArea.Plugin, {
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : '2.4',
+                       version         : '3.0',
                        developer       : 'Stanislas Rolland',
                        developerUrl    : 'http://www.sjbr.ca/',
                        copyrightOwner  : 'Stanislas Rolland',
@@ -87,20 +87,19 @@ HTMLArea.Acronym = Ext.extend(HTMLArea.Plugin, {
                        // Could be a button or its hotkey
                var buttonId = this.translateHotKey(id);
                buttonId = buttonId ? buttonId : id;
-               var selection = editor._getSelection();
-               var abbr = editor._activeElement(selection);
+               var abbr = editor.getSelection().getParentElement();
                        // Working around Safari issue
                if (!abbr && this.editor.statusBar && this.editor.statusBar.getSelection()) {
                        abbr = this.editor.statusBar.getSelection();
                }
                if (!abbr || !/^(acronym|abbr)$/i.test(abbr.nodeName)) {
-                       abbr = editor._getFirstAncestor(selection, ['acronym', 'abbr']);
+                       abbr = editor.getSelection().getFirstAncestorOfType(['acronym', 'abbr']);
                }
                var type = !Ext.isEmpty(abbr) ? abbr.nodeName.toLowerCase() : '';
                this.params = {
                        abbr: abbr,
                        title: !Ext.isEmpty(abbr) ? abbr.title : '',
-                       text: !Ext.isEmpty(abbr) ? abbr.innerHTML : this.editor.getSelectedHTML()
+                       text: !Ext.isEmpty(abbr) ? abbr.innerHTML : this.editor.getSelection().getHtml()
                };
                        // Open the dialogue window
                this.openDialogue(
@@ -514,7 +513,7 @@ HTMLArea.Acronym = Ext.extend(HTMLArea.Plugin, {
                        if (language) {
                                this.getPluginInstance('Language').setLanguageAttributes(abbr, language);
                        }
-                       this.editor.insertNodeAtSelection(abbr);
+                       this.editor.getSelection().insertNode(abbr);
                } else {
                        var abbr = this.params.abbr;
                        abbr.title = tab.find('itemId', 'useTerm')[0].getValue();
@@ -535,7 +534,7 @@ HTMLArea.Acronym = Ext.extend(HTMLArea.Plugin, {
                this.restoreSelection();
                var abbr = this.params.abbr;
                if (abbr) {
-                       this.editor.removeMarkup(abbr);
+                       this.editor.getDomNode().removeMarkup(abbr);
                }
                this.close();
                event.stopEvent();
@@ -545,7 +544,7 @@ HTMLArea.Acronym = Ext.extend(HTMLArea.Plugin, {
         */
        onUpdateToolbar: function (button, mode, selectionEmpty, ancestors) {
                if ((mode === 'wysiwyg') && this.editor.isEditable()) {
-                       var el = this.editor.getParentElement();
+                       var el = this.editor.getSelection().getParentElement();
                        if (el) {
                                button.setDisabled(((el.nodeName.toLowerCase() == 'acronym' && this.pageTSConfiguration.noAcronym) || (el.nodeName.toLowerCase() == 'abbr' && this.pageTSConfiguration.noAbbr)));
                                button.setInactive(!(el.nodeName.toLowerCase() == 'acronym' && !this.pageTSConfiguration.noAcronym) && !(el.nodeName.toLowerCase() == 'abbr' && !this.pageTSConfiguration.noAbbr));
index 38e0f40..868b3eb 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2007-2011 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  (c) 2007-2012 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -97,7 +97,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : '2.0',
+                       version         : '3.0',
                        developer       : 'Stanislas Rolland',
                        developerUrl    : 'http://www.sjbr.ca/',
                        copyrightOwner  : 'Stanislas Rolland',
@@ -213,10 +213,9 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
        /*
         * The function returns true if the type of block element is allowed in the current configuration
         */
-       isAllowedBlockElement : function (blockName) {
+       isAllowedBlockElement: function (blockName) {
                return this.allowedBlockElements.test(blockName);
        },
-       
        /*
         * This function adds an attribute to the array of attributes allowed on block elements
         *
@@ -224,18 +223,16 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      void
         */
-       addAllowedAttribute : function (attribute) {
+       addAllowedAttribute: function (attribute) {
                this.allowedAttributes.push(attribute);
        },
-       
        /*
         * This function gets called when some block element was selected in the drop-down list
         */
-       onChange : function (editor, combo, record, index) {
+       onChange: function (editor, combo, record, index) {
                this.applyBlockElement(combo.itemId, combo.getValue());
        },
-       
-       applyBlockElement : function(buttonId, blockElement) {
+       applyBlockElement: function (buttonId, blockElement) {
                var tagName = blockElement;
                var className = null;
                if (this.formatBlockItems[tagName]) {
@@ -263,14 +260,14 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        }
                                        this.editor.focus();
                                        if (Ext.isWebKit) {
-                                               if (!this.editor._doc.body.hasChildNodes()) {
-                                                       this.editor._doc.body.appendChild((this.editor._doc.createElement("br")));
+                                               if (!this.editor.document.body.hasChildNodes()) {
+                                                       this.editor.document.body.appendChild((this.editor.document.createElement("br")));
                                                }
                                                        // WebKit sometimes leaves empty block at the end of the selection
-                                               this.editor._doc.body.normalize();
+                                               this.editor.document.body.normalize();
                                        }
                                        try {
-                                               this.editor._doc.execCommand(buttonId, false, element);
+                                               this.editor.getSelection().execCommand(buttonId, false, element);
                                        } catch(e) {
                                                this.appendToLog('applyBlockElement', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                        }
@@ -278,7 +275,6 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        }
                }
        },
-       
        /*
         * This function gets called when a button was pressed.
         *
@@ -289,22 +285,20 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      boolean         false if action is completed
         */
-       onButtonPress : function (editor, id, target, className) {
+       onButtonPress: function (editor, id, target, className) {
                        // Could be a button or its hotkey
                var buttonId = this.translateHotKey(id);
                buttonId = buttonId ? buttonId : id;
-               this.editor.focus();
-               var selection = editor._getSelection();
-               var range = editor._createRange(selection);
+               var range = editor.getSelection().createRange();
                var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
-               var parentElement = statusBarSelection ? statusBarSelection : this.editor.getParentElement(selection, range);
+               var parentElement = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
                if (target) {
                        parentElement = target;
                }
-               while (parentElement && (!HTMLArea.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName))) {
+               while (parentElement && (!HTMLArea.DOM.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName))) {
                        parentElement = parentElement.parentNode;
                }
-               var blockAncestors = this.getBlockAncestors(parentElement);
+               var blockAncestors = HTMLArea.DOM.getBlockAncestors(parentElement);
                var tableCell = null;
                if (id === "TAB" || id === "SHIFT-TAB") {
                        for (var i = blockAncestors.length; --i >= 0;) {
@@ -320,13 +314,13 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                if (/^(ol|ul)$/i.test(parentElement.nodeName) && !(fullNodeTextSelected && !/^(li)$/i.test(parentElement.parentNode.nodeName))) {
                                        if (Ext.isOpera) {
                                                try {
-                                                       this.editor._doc.execCommand(buttonId, false, null);
+                                                       this.editor.getSelection().execCommand(buttonId, false, null);
                                                } catch(e) {
                                                        this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                                }
                                                this.indentedList = parentElement;
                                                this.makeNestedList(parentElement);
-                                               this.editor.selectNodeContents(this.indentedList.lastChild, false);
+                                               this.editor.getSelection().selectNodeContents(this.indentedList.lastChild, false);
                                        } else {
                                                this.indentSelectedListElements(parentElement, range);
                                        }
@@ -345,12 +339,12 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                        nextCell = tablePart.nextSibling ? tablePart.nextSibling.rows[0].cells[0] : null;
                                                        break;
                                                    case "tfoot":
-                                                       this.editor.selectNodeContents(tablePart.parentNode.lastChild.lastChild.lastChild, true);
+                                                       this.editor.getSelection().selectNodeContents(tablePart.parentNode.lastChild.lastChild.lastChild, true);
                                                }
                                        }
                                        if (!nextCell) {
-                                               if (this.editor.plugins.TableOperations) {
-                                                       this.editor.plugins.TableOperations.instance.onButtonPress(this.editor, "TO-row-insert-under");
+                                               if (this.getPluginInstance('TableOperations')) {
+                                                       this.getPluginInstance('TableOperations').onButtonPress(this.editor, 'TO-row-insert-under');
                                                } else {
                                                        nextCell = tablePart.parentNode.rows[0].cells[0];
                                                }
@@ -359,11 +353,11 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                if (Ext.isOpera && !nextCell.hasChildNodes()) {
                                                        nextCell.appendChild(this.editor.document.createElement('br'));
                                                }
-                                               this.editor.selectNodeContents(nextCell, true);
+                                               this.editor.getSelection().selectNodeContents(nextCell, true);
                                        }
                                } else  if (this.useBlockquote) {
                                        try {
-                                               this.editor._doc.execCommand(buttonId, false, null);
+                                               this.editor.getSelection().execCommand(buttonId, false, null);
                                        } catch(e) {
                                                this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                        }
@@ -373,9 +367,9 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        } else if (!/^div$/i.test(parentElement.nodeName) && /^div$/i.test(parentElement.parentNode.nodeName) && !HTMLArea.DOM.hasClass(parentElement.parentNode, this.useClass[buttonId])) {
                                                HTMLArea.DOM.addClass(parentElement.parentNode, this.useClass[buttonId]);
                                        } else {
-                                               var bookmark = this.editor.getBookmark(range);
-                                               var newBlock = this.wrapSelectionInBlockElement("div", this.useClass[buttonId], null, true);
-                                               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+                                               var bookmark = this.editor.getBookMark().get(range);
+                                               var newBlock = this.wrapSelectionInBlockElement('div', this.useClass[buttonId], null, true);
+                                               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
                                        }
                                } else {
                                        this.addClassOnBlockElements(buttonId);
@@ -386,7 +380,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        if (/^(li)$/i.test(parentElement.parentNode.nodeName)) {
                                                if (Ext.isOpera) {
                                                        try {
-                                                               this.editor._doc.execCommand(buttonId, false, null);
+                                                               this.editor.getSelection().execCommand(buttonId, false, null);
                                                        } catch(e) {
                                                                this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                                        }
@@ -418,23 +412,23 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                if (Ext.isOpera && !previousCell.hasChildNodes()) {
                                                        previousCell.appendChild(this.editor.document.createElement('br'));
                                                }
-                                               this.editor.selectNodeContents(previousCell, true);
+                                               this.editor.getSelection().selectNodeContents(previousCell, true);
                                        }
                                } else  if (this.useBlockquote) {
                                        try {
-                                               this.editor._doc.execCommand(buttonId, false, null);
+                                               this.editor.getSelection().execCommand(buttonId, false, null);
                                        } catch(e) {
                                                this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                        }
                                } else if (this.isAllowedBlockElement("div")) {
                                        for (var i = blockAncestors.length; --i >= 0;) {
                                                if (HTMLArea.DOM.hasClass(blockAncestors[i], this.useClass.Indent)) {
-                                                       var bookmark = this.editor.getBookmark(range);
-                                                       var newBlock = this.wrapSelectionInBlockElement("div", false, blockAncestors[i]);
+                                                       var bookmark = this.editor.getBookMark().get(range);
+                                                       var newBlock = this.wrapSelectionInBlockElement('div', false, blockAncestors[i]);
                                                                // If not directly under the div, we need to backtrack
                                                        if (newBlock.parentNode !== blockAncestors[i]) {
                                                                var parent = newBlock.parentNode;
-                                                               this.removeElement(newBlock);
+                                                               this.editor.getDomNode().removeMarkup(newBlock);
                                                                while (parent.parentNode !== blockAncestors[i]) {
                                                                        parent = parent.parentNode;
                                                                }
@@ -445,7 +439,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                        HTMLArea.DOM.removeClass(newBlock, this.useClass.Indent);
                                                        if (!newBlock.previousSibling) {
                                                                while (newBlock.hasChildNodes()) {
-                                                                       if (newBlock.firstChild.nodeType == 1) {
+                                                                       if (newBlock.firstChild.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
                                                                                newBlock.firstChild.className = newBlock.className;
                                                                        }
                                                                        blockAncestors[i].parentNode.insertBefore(newBlock.firstChild, blockAncestors[i]);
@@ -453,14 +447,14 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                        } else if (!newBlock.nextSibling) {
                                                                if (blockAncestors[i].nextSibling) {
                                                                        while (newBlock.hasChildNodes()) {
-                                                                               if (newBlock.firstChild.nodeType == 1) {
+                                                                               if (newBlock.firstChild.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
                                                                                        newBlock.lastChild.className = newBlock.className;
                                                                                }
                                                                                blockAncestors[i].parentNode.insertBefore(newBlock.lastChild, blockAncestors[i].nextSibling);
                                                                        }
                                                                } else {
                                                                        while (newBlock.hasChildNodes()) {
-                                                                               if (newBlock.firstChild.nodeType == 1) {
+                                                                               if (newBlock.firstChild.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
                                                                                        newBlock.firstChild.className = newBlock.className;
                                                                                }
                                                                                blockAncestors[i].parentNode.appendChild(newBlock.firstChild);
@@ -477,7 +471,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                                        clone.appendChild(newBlock.nextSibling);
                                                                }
                                                                while (newBlock.hasChildNodes()) {
-                                                                       if (newBlock.firstChild.nodeType == 1) {
+                                                                       if (newBlock.firstChild.nodeType === HTMLArea.DOM.ELEMENT_NODE) {
                                                                                newBlock.firstChild.className = newBlock.className;
                                                                        }
                                                                        blockAncestors[i].parentNode.insertBefore(newBlock.firstChild, clone);
@@ -487,7 +481,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                                        if (!blockAncestors[i].hasChildNodes()) {
                                                                blockAncestors[i].parentNode.removeChild(blockAncestors[i]);
                                                        }
-                                                       this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+                                                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
                                                        break;
                                                }
                                        }
@@ -504,21 +498,21 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                for (var i = blockAncestors.length; --i >= 0;) {
                                        if (/^blockquote$/i.test(blockAncestors[i].nodeName)) {
                                                commandState = true;
-                                               this.removeElement(blockAncestors[i]);
+                                               this.editor.getDomNode().removeMarkup(blockAncestors[i]);
                                                break;
                                        }
                                }
                                if (!commandState) {
-                                       var bookmark = this.editor.getBookmark(range);
-                                       var newBlock = this.wrapSelectionInBlockElement("blockquote", className, null, true);
-                                       this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+                                       var bookmark = this.editor.getBookMark().get(range);
+                                       var newBlock = this.wrapSelectionInBlockElement('blockquote', className, null, true);
+                                       this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
                                }
                                break;
                        case "address" :
                        case "div"     :
-                               var bookmark = this.editor.getBookmark(range);
+                               var bookmark = this.editor.getBookMark().get(range);
                                var newBlock = this.wrapSelectionInBlockElement(buttonId, className, null, true);
-                               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+                               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
                                break;
                        case "JustifyLeft"   :
                        case "JustifyCenter" :
@@ -526,7 +520,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        case "JustifyFull"   :
                                if (this.useAlignAttribute) {
                                        try {
-                                               this.editor._doc.execCommand(buttonId, false, null);
+                                               this.editor.getSelection().execCommand(buttonId, false, null);
                                        } catch(e) {
                                                this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                                        }
@@ -543,7 +537,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                break;
                        case "none" :
                                if (this.isAllowedBlockElement(parentElement.nodeName)) {
-                                       this.removeElement(parentElement);
+                                       this.editor.getDomNode().removeMarkup(parentElement);
                                }
                                break;
                        default :
@@ -551,23 +545,6 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                }
                return false;
        },
-       
-       /*
-       * Get the block ancestors of an element within a given block
-       */
-       getBlockAncestors : function(element, withinBlock) {
-               var ancestors = new Array();
-               var ancestor = element;
-               while (ancestor && (ancestor.nodeType === 1) && !/^(body)$/i.test(ancestor.nodeName) && ancestor != withinBlock) {
-                       if (HTMLArea.isBlockElement(ancestor)) {
-                               ancestors.unshift(ancestor);
-                       }
-                       ancestor = ancestor.parentNode;
-               }
-               ancestors.unshift(ancestor);
-               return ancestors;
-       },
-       
        /*
         * This function wraps the block elements intersecting the current selection in a block element of the given type
         *
@@ -578,10 +555,10 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
         *
         * @return      object          the wrapping block
         */
-       wrapSelectionInBlockElement : function(blockName, useClass, withinBlock, keepValid) {
-               var endBlocks = this.editor.getEndBlocks(this.editor._getSelection());
-               var startAncestors = this.getBlockAncestors(endBlocks.start, withinBlock);
-               var endAncestors = this.getBlockAncestors(endBlocks.end, withinBlock);
+       wrapSelectionInBlockElement: function (blockName, useClass, withinBlock, keepValid) {
+               var endBlocks = this.editor.getSelection().getEndBlocks();
+               var startAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.start, withinBlock);
+               var endAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.end, withinBlock);
                var i = 0;
                while (i < startAncestors.length && i < endAncestors.length && startAncestors[i] === endAncestors[i]) {
                        ++i;
@@ -601,7 +578,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                }
                        }
                }
-               var blockElement = this.editor._doc.createElement(blockName);
+               var blockElement = this.editor.document.createElement(blockName);
                if (useClass) {
                        HTMLArea.DOM.addClass(blockElement, useClass);
                }
@@ -634,19 +611,17 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                }
                        // Things go wrong in some browsers when the node is empty
                if (Ext.isWebKit && !blockElement.hasChildNodes()) {
-                       blockElement = blockElement.appendChild(this.editor._doc.createElement("br"));
+                       blockElement = blockElement.appendChild(this.editor.document.createElement('br'));
                }
                return blockElement;
        },
-       
        /*
         * This function adds a class attribute on blocks sibling of the block containing the start container of the selection
         */
-       addClassOnBlockElements : function(buttonId, className) {
-               var selection = this.editor._getSelection();
-               var endBlocks = this.editor.getEndBlocks(selection);
-               var startAncestors = this.getBlockAncestors(endBlocks.start);
-               var endAncestors = this.getBlockAncestors(endBlocks.end);
+       addClassOnBlockElements: function (buttonId, className) {
+               var endBlocks = this.editor.getSelection().getEndBlocks();
+               var startAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.start);
+               var endAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.end);
                var index = 0;
                while (index < startAncestors.length && index < endAncestors.length && startAncestors[index] === endAncestors[index]) {
                        ++index;
@@ -656,7 +631,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                }
                if (!/^(body)$/i.test(startAncestors[index].nodeName)) {
                        for (var block = startAncestors[index]; block; block = block.nextSibling) {
-                               if (HTMLArea.isBlockElement(block)) {
+                               if (HTMLArea.DOM.isBlockElement(block)) {
                                        switch (buttonId) {
                                                case "Indent" :
                                                        if (!HTMLArea.DOM.hasClass(block, this.useClass[buttonId])) {
@@ -690,11 +665,10 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        }
                }
        },
-       
        /*
         * This function toggles the alignment class on the given block
         */
-       toggleAlignmentClass : function(block, buttonId) {
+       toggleAlignmentClass: function (block, buttonId) {
                for (var alignmentButtonId in this.useClass) {
                        if (this.useClass.hasOwnProperty(alignmentButtonId) && alignmentButtonId !== "Indent") {
                                if (HTMLArea.DOM.hasClass(block, this.useClass[alignmentButtonId])) {
@@ -704,70 +678,40 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                }
                        }
                }
-               if (/^div$/i.test(block.nodeName) && !this.hasAllowedAttributes(block)) {
-                       this.removeElement(block);
-               }
-       },
-       
-       /*
-        * This function verifies if the element has any of the allowed attributes
-        */
-       hasAllowedAttributes : function(element) {
-               for (var i = 0; i < this.allowedAttributes.length; ++i) {
-                       if (element.getAttribute(this.allowedAttributes[i])) {
-                               return true;
-                       }
-               }
-               return false;
-       },
-       
-       /*
-        * This function removes the given element but keeps its contents
-        */
-       removeElement : function(element) {
-               var selection = this.editor._getSelection();
-               var range = this.editor._createRange(selection);
-               var lastChild;
-               var bookmark = this.editor.getBookmark(range);
-               var parent = element.parentNode;
-               while (element.firstChild) {
-                       lastChild = parent.insertBefore(element.firstChild, element);
+               if (/^div$/i.test(block.nodeName) && !HTMLArea.DOM.hasAllowedAttributes(block, this.allowedAttributes)) {
+                       this.editor.getDomNode().removeMarkup(block);
                }
-               parent.removeChild(element);
-               var range = this.editor.moveToBookmark(bookmark);
-               this.editor.selectRange(range);
        },
-       
-       insertList : function (buttonId, parentElement) {
+
+       insertList: function (buttonId, parentElement) {
                if (/^(dd)$/i.test(parentElement.nodeName)) {
-                       var list = parentElement.appendChild(this.editor._doc.createElement((buttonId === "OrderedList") ? "ol" : "ul"));
-                       var first = list.appendChild(this.editor._doc.createElement("li"));
-                       first.innerHTML = "<br />";
-                       this.editor.selectNodeContents(first,true);
+                       var list = parentElement.appendChild(this.editor.document.createElement((buttonId === 'OrderedList') ? 'ol' : 'ul'));
+                       var first = list.appendChild(this.editor.document.createElement('li'));
+                       first.innerHTML = '<br />';
+                       this.editor.getSelection().selectNodeContents(first, true);
                } else {
                                // parentElement may be removed by following command
                        var parentNode = parentElement.parentNode;
                        try {
-                               this.editor._doc.execCommand(buttonId, false, null);
+                               this.editor.getSelection().execCommand(buttonId, false, null);
                        } catch(e) {
                                this.appendToLog('onButtonPress', e + '\n\nby execCommand(' + buttonId + ');', 'error');
                        }
                        if (Ext.isWebKit) {
-                               this.editor.cleanAppleStyleSpans(parentNode);
+                               this.editor.getDomNode().cleanAppleStyleSpans(parentNode);
                        }
                }
        },
-       
        /*
         * Indent selected list elements
         */
-       indentSelectedListElements : function (list, range) {
-               var bookmark = this.editor.getBookmark(range);
+       indentSelectedListElements: function (list, range) {
+               var bookmark = this.editor.getBookMark().get(range);
                        // The selected elements are wrapped into a list element
                var indentedList = this.wrapSelectionInBlockElement(list.nodeName.toLowerCase(), null, list);
                        // which breaks the range
-               var range = this.editor.moveToBookmark(bookmark);
-               bookmark = this.editor.getBookmark(range);
+               var range = this.editor.getBookMark().moveTo(bookmark);
+               bookmark = this.editor.getBookMark().get(range);
                
                        // Check if the last element has children. If so, outdent those that do not intersect the selection
                var last = indentedList.lastChild.lastChild;
@@ -775,13 +719,13 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        var child = last.firstChild, next;
                        while (child) {
                                next = child.nextSibling;
-                               if (!this.editor.rangeIntersectsNode(range, child)) {
+                               if (!HTMLArea.DOM.rangeIntersectsNode(range, child)) {
                                        indentedList.appendChild(child);
                                }
                                child = next;
                        }
                        if (!last.hasChildNodes()) {
-                               HTMLArea.removeFromParent(last);
+                               HTMLArea.DOM.removeFromParent(last);
                        }
                }
                if (indentedList.previousSibling && indentedList.previousSibling.hasChildNodes()) {
@@ -798,24 +742,23 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        }
                } else {
                                // Indenting the first element and possibly some more
-                       var first = this.editor._doc.createElement("li");
+                       var first = this.editor.document.createElement("li");
                        first.innerHTML = "&nbsp;";
                        list.insertBefore(first, indentedList);
                        indentedList = first.appendChild(indentedList);
                }
-               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
        },
-       
        /*
         * Outdent selected list elements
         */
-       outdentSelectedListElements : function (list, range) {
+       outdentSelectedListElements: function (list, range) {
                        // We wrap the selected li elements and thereafter move them one level up
-               var bookmark = this.editor.getBookmark(range);
+               var bookmark = this.editor.getBookMark().get(range);
                var wrappedList = this.wrapSelectionInBlockElement(list.nodeName.toLowerCase(), null, list);
                        // which breaks the range
-               var range = this.editor.moveToBookmark(bookmark);
-               bookmark = this.editor.getBookmark(range);
+               var range = this.editor.getBookMark().moveTo(bookmark);
+               bookmark = this.editor.getBookMark().get(range);
                
                if (!wrappedList.previousSibling) {
                                // Outdenting the first element(s) of an indented list
@@ -833,7 +776,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                } else if (!wrappedList.nextSibling) {
                                // Outdenting the last element(s) of the list
                                // This will break the gecko bookmark
-                       this.editor.moveToBookmark(bookmark);
+                       this.editor.getBookMark().moveTo(bookmark);
                        while (wrappedList.hasChildNodes()) {
                                if (list.parentNode.nextSibling) {
                                        list.parentNode.parentNode.insertBefore(wrappedList.firstChild, list.parentNode.nextSibling);
@@ -842,8 +785,8 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                }
                        }
                        list.removeChild(wrappedList);
-                       this.editor.selectNodeContents(list.parentNode.nextSibling, true);
-                       bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
+                       this.editor.getSelection().selectNodeContents(list.parentNode.nextSibling, true);
+                       bookmark = this.editor.getBookMark().get(this.editor.getSelection().createRange());
                } else {
                                // Outdenting the middle of a list
                        var next = list.parentNode.nextSibling;
@@ -866,14 +809,13 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                if (!list.hasChildNodes()) {
                        list.parentNode.removeChild(list);
                } 
-               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
+               this.editor.getSelection().selectRange(this.editor.getBookMark().moveTo(bookmark));
        },
-       
        /*
         * Make XHTML-compliant nested list
         * We need this for Opera
         */
-       makeNestedList : function(el) {
+       makeNestedList: function (el) {
                var previous;
                for (var i = el.firstChild; i; i = i.nextSibling) {
                        if (/^li$/i.test(i.nodeName)) {
@@ -886,24 +828,23 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                previous = i.previousSibling;
                                this.indentedList = i.cloneNode(true);
                                if (!previous) {
-                                       previous = el.insertBefore(this.editor._doc.createElement("li"), i);
+                                       previous = el.insertBefore(this.editor.document.createElement('li'), i);
                                        this.indentedList = previous.appendChild(this.indentedList);
                                } else {
                                        this.indentedList = previous.appendChild(this.indentedList);
                                }
-                               HTMLArea.removeFromParent(i);
+                               HTMLArea.DOM.removeFromParent(i);
                                this.makeNestedList(el);
                                break;
                        }
                }
        },
-       
        /*
         * Insert a paragraph
         */
-       insertParagraph : function(after) {
-               var endBlocks = this.editor.getEndBlocks(this.editor._getSelection());
-               var ancestors = after ? this.getBlockAncestors(endBlocks.end) : this.getBlockAncestors(endBlocks.start);
+       insertParagraph: function (after) {
+               var endBlocks = this.editor.getSelection().getEndBlocks();
+               var ancestors = after ? HTMLArea.DOM.getBlockAncestors(endBlocks.end) : HTMLArea.DOM.getBlockAncestors(endBlocks.start);
                var endElement = ancestors[ancestors.length-1];
                for (var i = ancestors.length; --i >= 0;) {
                        if (/^(table|div|ul|ol|dl|blockquote|address|pre)$/i.test(ancestors[i].nodeName) && !/^(li)$/i.test(ancestors[i].parentNode.nodeName)) {
@@ -913,41 +854,41 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                }
                if (endElement) {
                        var parent = endElement.parentNode;
-                       var paragraph = this.editor._doc.createElement("p");
+                       var paragraph = this.editor.document.createElement("p");
                        if (Ext.isIE) {
                                paragraph.innerHTML = "&nbsp";
                        } else {
-                               paragraph.appendChild(this.editor._doc.createElement("br"));
+                               paragraph.appendChild(this.editor.document.createElement("br"));
                        }
                        if (after && !endElement.nextSibling) {
                                parent.appendChild(paragraph);
                        } else {
                                parent.insertBefore(paragraph, after ? endElement.nextSibling : endElement);
                        }
-                       this.editor.selectNodeContents(paragraph, true);
+                       this.editor.getSelection().selectNodeContents(paragraph, true);
                }
        },
        /*
         * Insert horizontal line
         */
-       insertHorizontalRule: function() {
-               this.editor.execCommand('InsertHorizontalRule');
+       insertHorizontalRule: function () {
+               this.editor.getSelection().execCommand('InsertHorizontalRule');
                        // Apply enterParagraphs rule
                if (!Ext.isIE && !Ext.isOpera && !this.editor.config.disableEnterParagraphs) {
-                       var range = this.editor._createRange(this.editor._getSelection());
+                       var range = this.editor.getSelection().createRange();
                        var startContainer = range.startContainer;
                        if (/^body$/i.test(startContainer.nodeName)) {
                                startContainer.normalize();
                                var ruler = startContainer.childNodes[range.startOffset-1];
                                if (ruler.nextSibling) {
-                                       if (ruler.nextSibling.nodeType == HTMLArea.DOM.TEXT_NODE) {
+                                       if (ruler.nextSibling.nodeType === HTMLArea.DOM.TEXT_NODE) {
                                                if (/\S/.test(ruler.nextSibling.textContent)) {
                                                        var paragraph = this.editor.document.createElement('p');
                                                        paragraph = startContainer.appendChild(paragraph);
                                                        paragraph = startContainer.insertBefore(paragraph, ruler.nextSibling);
                                                        paragraph.appendChild(ruler.nextSibling);
                                                } else {
-                                                       HTMLArea.removeFromParent(ruler.nextSibling);
+                                                       HTMLArea.DOM.removeFromParent(ruler.nextSibling);
                                                        var paragraph = ruler.nextSibling;
                                                }
                                        } else {
@@ -966,7 +907,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        }
                                        paragraph = startContainer.appendChild(paragraph);
                                }
-                               this.editor.selectNodeContents(paragraph, true);
+                               this.editor.getSelection().selectNodeContents(paragraph, true);
                        }
                }
        },
@@ -994,21 +935,20 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
         * @return      boolean         false, if the event was taken care of
         */
        onKey: function (key, event) {
-               var selection = this.editor._getSelection();
-               if (this.editor._selectionEmpty(selection)) {
-                       var range = this.editor._createRange(selection);
-                       var parentElement = this.editor.getParentElement(selection, range);
-                       while (parentElement && !HTMLArea.isBlockElement(parentElement)) {
+               if (this.editor.getSelection().isEmpty()) {
+                       var range = this.editor.getSelection().createRange();
+                       var parentElement = this.editor.getSelection().getParentElement();
+                       while (parentElement && !HTMLArea.DOM.isBlockElement(parentElement)) {
                                parentElement = parentElement.parentNode;
                        }
                        if (/^(dt|dd)$/i.test(parentElement.nodeName)) {
-                               var nodeRange = this.editor._createRange();
+                               var nodeRange = this.editor.getSelection().createRange();
                                nodeRange.moveToElementText(parentElement);
                                range.setEndPoint("EndToEnd", nodeRange);
                                if (!range.text || range.text == "\x20") {
-                                       var item = parentElement.parentNode.insertBefore(this.editor._doc.createElement((parentElement.nodeName.toLowerCase() === "dt") ? "dd" : "dt"), parentElement.nextSibling);
+                                       var item = parentElement.parentNode.insertBefore(this.editor.document.createElement((parentElement.nodeName.toLowerCase() === "dt") ? "dd" : "dt"), parentElement.nextSibling);
                                        item.innerHTML = "\x20";
-                                       this.editor.selectNodeContents(item, true);
+                                       this.editor.getSelection().selectNodeContents(item, true);
                                        event.stopEvent();
                                        return false;
                                }
@@ -1016,8 +956,8 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        && !parentElement.innerText
                                        && parentElement.parentNode.parentNode
                                        && /^(dd|td|th)$/i.test(parentElement.parentNode.parentNode.nodeName)) {
-                               var item = parentElement.parentNode.parentNode.insertBefore(this.editor._doc.createTextNode("\x20"), parentElement.parentNode.nextSibling);
-                               this.editor.selectNodeContents(parentElement.parentNode.parentNode, false);
+                               var item = parentElement.parentNode.parentNode.insertBefore(this.editor.document.createTextNode("\x20"), parentElement.parentNode.nextSibling);
+                               this.editor.getSelection().selectNodeContents(parentElement.parentNode.parentNode, false);
                                parentElement.parentNode.removeChild(parentElement);
                                event.stopEvent();
                                return false;
@@ -1028,7 +968,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
        /*
         * This function removes any disallowed class or mutually exclusive classes from the class attribute of the node
         */
-       cleanClasses : function(node) {
+       cleanClasses: function (node) {
                var classNames = node.className.trim().split(" ");
                var nodeName = node.nodeName.toLowerCase();
                for (var i = classNames.length; --i >= 0;) {
@@ -1048,22 +988,21 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        }
                }
        },
-       
        /*
         * This function gets called when the toolbar is updated
         */
        onUpdateToolbar: function (button, mode, selectionEmpty, ancestors, endPointsInSameBlock) {
                if (mode === 'wysiwyg' && this.editor.isEditable()) {
                        var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
-                       var parentElement = statusBarSelection ? statusBarSelection : this.editor.getParentElement();
+                       var parentElement = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
                        if (!/^body$/i.test(parentElement.nodeName)) {
-                               while (parentElement && !HTMLArea.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName)) {
+                               while (parentElement && !HTMLArea.DOM.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName)) {
                                        parentElement = parentElement.parentNode;
                                }
-                               var blockAncestors = this.getBlockAncestors(parentElement);
-                               var endBlocks = this.editor.getEndBlocks(this.editor._getSelection());
-                               var startAncestors = this.getBlockAncestors(endBlocks.start);
-                               var endAncestors = this.getBlockAncestors(endBlocks.end);
+                               var blockAncestors = HTMLArea.DOM.getBlockAncestors(parentElement);
+                               var endBlocks = this.editor.getSelection().getEndBlocks();
+                               var startAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.start);
+                               var endAncestors = HTMLArea.DOM.getBlockAncestors(endBlocks.end);
                                var index = 0;
                                while (index < startAncestors.length && index < endAncestors.length && startAncestors[index] === endAncestors[index]) {
                                        ++index;
@@ -1117,7 +1056,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        case "JustifyFull"   :
                                                if (this.useAlignAttribute) {
                                                        try {
-                                                               commandState = this.editor._doc.queryCommandState(button.itemId);
+                                                               commandState = this.editor.document.queryCommandState(button.itemId);
                                                        } catch(e) {
                                                                commandState = false;
                                                        }
@@ -1140,7 +1079,7 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                                        case "InsertOrderedList":
                                        case "InsertUnorderedList":
                                                try {
-                                                       commandState = this.editor._doc.queryCommandState(button.itemId);
+                                                       commandState = this.editor.document.queryCommandState(button.itemId);
                                                } catch(e) {
                                                        commandState = false;
                                                }
@@ -1184,11 +1123,10 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        }
                }
        },
-       
        /*
         * This function updates the drop-down list of block elements
         */
-       updateDropDown : function(select, deepestBlockAncestor, startAncestor) {
+       updateDropDown: function(select, deepestBlockAncestor, startAncestor) {
                var store = select.getStore();
                store.removeAt(0);
                var index = -1;
@@ -1218,11 +1156,10 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                        select.setValue(store.getAt(index+1).get('value'));
                }
        },
-       
        /*
        * This function handles the hotkey events registered on elements of the dropdown list
        */      
-       onHotKey : function(editor, key) {
+       onHotKey: function(editor, key) {
                var blockElement;
                var hotKeyConfiguration = this.getHotKeyConfiguration(key);
                if (hotKeyConfiguration) {
@@ -1235,4 +1172,3 @@ HTMLArea.BlockElements = Ext.extend(HTMLArea.Plugin, {
                return true;
        }
 });
-
index 5fa0d4b..0617c74 100644 (file)
@@ -87,7 +87,7 @@ HTMLArea.BlockStyle = Ext.extend(HTMLArea.Plugin, {
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : '2.1',
+                       version         : '3.0',
                        developer       : 'Stanislas Rolland',
                        developerUrl    : 'http://www.sjbr.ca/',
                        copyrightOwner  : 'Stanislas Rolland',
@@ -133,10 +133,10 @@ HTMLArea.BlockStyle = Ext.extend(HTMLArea.Plugin, {
        onChange: function (editor, combo, record, index) {
                var className = combo.getValue();
                this.editor.focus();
-               var blocks = this.getSelectedBlocks();
+               var blocks = this.editor.getSelection().getElements();
                for (var k = 0; k < blocks.length; ++k) {
                        var parent = blocks[k];
-                       while (parent && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") {
+                       while (parent && !HTMLArea.DOM.isBlockElement(parent) && !/^(img)$/i.test(parent.nodeName)) {
                                parent = parent.parentNode;
                        }
                        if (!k) {
@@ -182,27 +182,6 @@ HTMLArea.BlockStyle = Ext.extend(HTMLArea.Plugin, {
                }
        },
        /*
-        * This function gets the list of selected blocks
-        */
-       getSelectedBlocks: function () {
-               var block, range, i = 0, blocks = [];
-               var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
-               if (Ext.isGecko) {
-                       var selection = this.editor._getSelection();
-                       try {
-                               while ((range = selection.getRangeAt(i++))) {
-                                       block = this.editor.getParentElement(selection, range);
-                                       blocks.push(statusBarSelection ? statusBarSelection : block);
-                               }
-                       } catch(e) {
-                               /* finished walking through selection */
-                       }
-               } else {
-                       blocks.push(statusBarSelection ? statusBarSelection : this.editor.getParentElement());
-               }
-               return blocks;
-       },
-       /*
         * This handler gets called when the editor is generated
         */
        onGenerate: function () {
@@ -261,8 +240,8 @@ HTMLArea.BlockStyle = Ext.extend(HTMLArea.Plugin, {
                        var classNames = new Array();
                        var tagName = null;
                        var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
-                       var parent = statusBarSelection ? statusBarSelection : this.editor.getParentElement();
-                       while (parent && !HTMLArea.isBlockElement(parent) && parent.nodeName.toLowerCase() != "img") {
+                       var parent = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
+                       while (parent && !HTMLArea.DOM.isBlockElement(parent) && !/^(img)$/i.test(parent.nodeName)) {
                                parent = parent.parentNode;
                        }
                        if (parent) {
index 81b5772..141eb0e 100644 (file)
@@ -40,7 +40,7 @@ HTMLArea.CharacterMap = Ext.extend(HTMLArea.Plugin, {
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : '3.0',
+                       version         : '4.0',
                        developer       : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
                        developerUrl    : 'http://www.sjbr.ca/',
                        copyrightOwner  : 'Holger Hees, Bernhard Pfeifer, Stanislas Rolland',
@@ -464,7 +464,6 @@ HTMLArea.CharacterMap = Ext.extend(HTMLArea.Plugin, {
         */
        insertCharacter: function (event, target) {
                event.stopEvent();
-               this.editor.focus();
                this.restoreSelection();
                var entity = Ext.get(target).dom.innerHTML;
                this.insertEntity(entity);
@@ -481,14 +480,13 @@ HTMLArea.CharacterMap = Ext.extend(HTMLArea.Plugin, {
         * @return      void
         */
        insertEntity: function (entity) {
-               this.editor.focus();
                if (Ext.isIE) {
-                       this.editor.insertHTML(entity);
+                       this.editor.getSelection().insertHtml(entity);
                } else {
                                // Firefox and WebKit convert '&nbsp;' to '&amp;nbsp;'
                        var node = this.editor.document.createTextNode(((Ext.isGecko || Ext.isWebKit) && entity == '&nbsp;') ? '\xA0' : entity);
-                       this.editor.insertNodeAtSelection(node);
-                       this.editor.selectNode(node, false);
+                       this.editor.getSelection().insertNode(node);
+                       this.editor.getSelection().selectNode(node, false);
                }
        },
        /*
@@ -496,7 +494,6 @@ HTMLArea.CharacterMap = Ext.extend(HTMLArea.Plugin, {
         *
         */
        resetFocus: function () {
-               this.editor.focus();
                this.restoreSelection();
        }
 });
index 6dfab8f..ddb7181 100644 (file)
@@ -2,7 +2,7 @@
 *  Copyright notice
 *
 *  Copyright (c) 2003 dynarch.com. Authored by Mihai Bazon. Sponsored by www.americanbible.org.
-*  Copyright (c) 2004-2011 Stanislas Rolland <typo3(arobas)sjbr.ca>
+*  Copyright (c) 2004-2012 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -178,7 +178,7 @@ HTMLArea.ContextMenu = Ext.extend(HTMLArea.Plugin, {
        showMenu: function (target) {
                this.showContextItems(target);
                if (!Ext.isIE) {
-                       this.ranges = this.editor.getSelectionRanges();
+                       this.ranges = this.editor.getSelection().getRanges();
                }
                var iframeEl = this.editor.iframe.getEl();
                        // Show the context menu
@@ -237,7 +237,7 @@ HTMLArea.ContextMenu = Ext.extend(HTMLArea.Plugin, {
         */
        onItemClick: function (item, event) {
                if (!Ext.isIE) {
-                       this.editor.setSelectionRanges(this.ranges);
+                       this.editor.getSelection().setRanges(this.ranges);
                }
                var button = this.getButton(item.getItemId());
                if (button) {
@@ -254,11 +254,11 @@ HTMLArea.ContextMenu = Ext.extend(HTMLArea.Plugin, {
                        var nextSibling = this.deleteTarget.nextSibling;
                        var previousSibling = this.deleteTarget.previousSibling;
                        if (nextSibling) {
-                               this.editor.selectNode(nextSibling, true);
+                               this.editor.getSelection().selectNode(nextSibling, true);
                        } else if (previousSibling) {
-                               this.editor.selectNode(previousSibling, false);
+                               this.editor.getSelection().selectNode(previousSibling, false);
                        }
-                       HTMLArea.removeFromParent(this.deleteTarget);
+                       HTMLArea.