Added feature #9521: htmlArea RTE: factor out some functions dealing with inline...
authorStanislas Rolland <typo3@sjbr.ca>
Wed, 8 Oct 2008 22:41:24 +0000 (22:41 +0000)
committerStanislas Rolland <typo3@sjbr.ca>
Wed, 8 Oct 2008 22:41:24 +0000 (22:41 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@4299 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
typo3/sysext/rtehtmlarea/ChangeLog
typo3/sysext/rtehtmlarea/htmlarea/htmlarea-gecko.js
typo3/sysext/rtehtmlarea/htmlarea/htmlarea-ie.js
typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/InlineElements/inline-elements.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TextStyle/text-style.js

index 5caee0e..3603001 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
 2008-10-08  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Fixed bug #9516: htmlARea RTE: Move link tags to head
+       * Added feature #9521: htmlArea RTE: factor out some functions dealing with inline elements
 
 2008-10-05  Stanislas Rolland  <typo3@sjbr.ca>
 
index 983f13e..3372578 100644 (file)
@@ -1,6 +1,7 @@
 2008-10-08  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Fixed bug #9516: htmlARea RTE: Move link tags to head
+       * Added feature #9521: htmlArea RTE: factor out some functions dealing with inline elements
 
 2008-10-05  Stanislas Rolland  <typo3@sjbr.ca>
 
index bae7822..164185b 100644 (file)
@@ -3,7 +3,7 @@
 *
 *  (c) 2002-2004, interactivetools.com, inc.
 *  (c) 2003-2004 dynarch.com
-*  (c) 2004-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+*  (c) 2004-2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -445,6 +445,40 @@ HTMLArea.prototype.insertHTML = function(html) {
        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.prototype.wrapWithInlineElement = function(element, selection, range) {
+               // Sometimes Opera raises a bad boundary points error
+       if (HTMLArea.is_opera) {
+               try {
+                       range.surroundContents(element);
+               } catch(e) {
+                       element.appendChild(range.extractContents());
+                       range.insertNode(element);
+               }
+       } else {
+               range.surroundContents(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);
+};
+
 /***************************************************
  *  EVENTS HANDLERS
  ***************************************************/
index 05173a6..90a75d5 100644 (file)
@@ -119,10 +119,12 @@ HTMLArea.prototype.getSelectedHTMLContents = function() {
 /*
  * Get the deepest node that contains both endpoints of the current selection.
  */
-HTMLArea.prototype.getParentElement = function(sel) {
-       if(!sel) var sel = this._getSelection();
-       var range = this._createRange(sel);
-       switch (sel.type) {
+HTMLArea.prototype.getParentElement = function(selection, range) {
+       if (!selection) var selection = this._getSelection();
+       if (typeof(range) === "undefined") {
+               var range = this._createRange(selection);
+       }
+       switch (selection.type) {
                case "Text":
                case "None":
                        var el = range.parentElement();
@@ -218,6 +220,82 @@ HTMLArea.prototype.insertHTML = function(html) {
        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.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
  ***************************************************/
index e3dde49..a4484d5 100644 (file)
@@ -1717,6 +1717,49 @@ HTMLArea.getElementObject = function(el,tagName) {
        return oEl;
 };
 
+/*
+ * This function removes the given markup element
+ *
+ * @param      object  element: the inline element to be removed, content being preserved
+ *
+ * @return     void
+ */
+HTMLArea.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
+ */
+HTMLArea.hasAllowedAttributes = function(element,allowedAttributes) {
+       var value;
+       for (var i = allowedAttributes.length; --i >= 0;) {
+               value = element.getAttribute(allowedAttributes[i]);
+               if (value) {
+                               // IE returns an object on getAttribute("style");
+                       if (typeof(value) == "object") {
+                               if (allowedAttributes[i] == "style" && value.cssText) {
+                                       return true;
+                               }
+                       } else {
+                               return true;
+                       }
+               }
+       }
+       return false;
+};
+
 /***************************************************
  *  SELECTIONS AND RANGES
  ***************************************************/
@@ -1774,6 +1817,22 @@ HTMLArea.prototype.getEndBlocks = function(selection) {
 };
 
 /*
+ * 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
+ */
+HTMLArea.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));
+       }
+};
+
+/*
  * Get the deepest ancestor of the selection that is of the specified type
  * Borrowed from Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
  */
index 78bcf02..8dc0040 100644 (file)
@@ -44,9 +44,14 @@ InlineElements = HTMLArea.Plugin.extend({
         * This function gets called by the base constructor
         */
        configurePlugin : function (editor) {
-               
-               this.allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", (HTMLArea.is_gecko?"class":"className"));
-               
+
+                       // Setting the array of allowed attributes on inline elements
+               if (this.editor.plugins.TextStyle && this.editor.plugins.TextStyle.instance) {
+                       this.allowedAttributes = this.editor.plugins.TextStyle.instance.allowedAttributes;
+               } else {
+                       this.allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", (HTMLArea.is_gecko?"class":"className"));
+               }
+                       // Getting tags configuration for inline elements
                if (this.editorConfiguration.buttons.textstyle) {
                        this.tags = this.editorConfiguration.buttons.textstyle.tags;
                }
@@ -168,6 +173,17 @@ InlineElements = HTMLArea.Plugin.extend({
        },
        
        /*
+        * This function adds an attribute to the array of allowed attributes on inline elements
+        *
+        * @param       string  attribute: the name of the attribute to be added to the array
+        *
+        * @return      void
+        */
+       addAllowedAttribute : function (attribute) {
+               this.allowedAttributes.push(attribute);
+       },
+       
+       /*
         * This function gets called when some inline element button was pressed.
         */
        onButtonPress : function (editor, id) {
@@ -205,7 +221,7 @@ InlineElements = HTMLArea.Plugin.extend({
                var elementIsAncestor = false;
                var selectionEmpty = editor._selectionEmpty(selection);
                if (HTMLArea.is_ie) {
-                       var bookmark = range.getBookmark();
+                       var bookmark = editor.getBookmark(range);
                }
                        // Check if the chosen element is among the ancestors
                for (var i = 0; i < ancestors.length; ++i) {
@@ -218,7 +234,7 @@ InlineElements = HTMLArea.Plugin.extend({
                if (!selectionEmpty) {
                                // The selection is not empty.
                        for (var i = 0; i < ancestors.length; ++i) {
-                               fullNodeSelected = (HTMLArea.is_ie && ((editor._statusBarTree.selected === ancestors[i] && ancestors[i].innerText === range.text) || (!editor._statusBarTree.selected && ancestors[i].innerText === range.text)))
+                               fullNodeSelected = (HTMLArea.is_ie && ((selection.type !== "Control" && ancestors[i].innerText === range.text) || (selection.type === "Control" && ancestors[i].innerText === range.item(0).text)))
                                                        || (HTMLArea.is_gecko && ((editor._statusBarTree.selected === ancestors[i] && ancestors[i].textContent === range.toString()) || (!editor._statusBarTree.selected && ancestors[i].textContent === range.toString())));
                                if (fullNodeSelected) {
                                        if (!HTMLArea.isBlockElement(ancestors[i])) {
@@ -240,43 +256,23 @@ InlineElements = HTMLArea.Plugin.extend({
                        }
                        if (element !== "none" && !(fullNodeSelected && elementIsAncestor)) {
                                        // Add markup
+                               var newElement = editor._doc.createElement(element);
+                               if (element === "bdo") {
+                                       newElement.setAttribute("dir", "rtl");
+                               }
                                if (HTMLArea.is_gecko) {
                                        if (fullNodeSelected && editor._statusBarTree.selected) {
                                                if (HTMLArea.is_safari) {
-                                                       this.editor.selectNode(parent);
-                                                       range = this.editor._createRange(this.editor._getSelection());
+                                                       editor.selectNode(parent);
+                                                       selection = editor._getSelection();
+                                                       range = editor._createRange(selection);
                                                } else {
                                                        range.selectNode(parent);
                                                }
                                        }
-                                       var newElement = this.editor._doc.createElement(element);
-                                       if (element === "bdo") {
-                                               newElement.setAttribute("dir", "rtl");
-                                       }
-                                               // Sometimes Opera 9.25 raises a bad boundary points error
-                                       if (HTMLArea.is_opera) {
-                                               try {
-                                                       range.surroundContents(newElement);
-                                               } catch(e) {
-                                                       newElement.appendChild(range.extractContents());
-                                                       range.insertNode(newElement);
-                                               }
-                                       } else {
-                                               range.surroundContents(newElement);
-                                       }
-                                               // Sometimes Firefox inserts empty elements just outside the boundaries of the range
-                                       var neighbour = newElement.previousSibling;
-                                       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-                                               HTMLArea.removeFromParent(neighbour);
-                                       }
-                                       neighbour = newElement.nextSibling;
-                                       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-                                               HTMLArea.removeFromParent(neighbour);
-                                       }
+                                       editor.wrapWithInlineElement(newElement, selection, range);
                                        if (fullNodeSelected && editor._statusBarTree.selected && !HTMLArea.is_safari) {
-                                               this.editor.selectNodeContents(newElement.lastChild, false);
-                                       } else {
-                                               this.editor.selectNodeContents(newElement, false);
+                                               editor.selectNodeContents(newElement.lastChild, false);
                                        }
                                        range.detach();
                                } else {
@@ -296,61 +292,7 @@ InlineElements = HTMLArea.Plugin.extend({
                                                        editor.selectNodeContents(newElement, false);
                                                }
                                        } else {
-                                               var rangeStart = range.duplicate();
-                                               rangeStart.collapse(true);
-                                               var parentStart = rangeStart.parentElement();
-                                               var rangeEnd = range.duplicate();
-                                               rangeEnd.collapse(true);
-                                               var newRange = editor._createRange();
-                                               
-                                               var parentEnd = rangeEnd.parentElement();
-                                               var upperParentStart = parentStart;
-                                               if (parentStart !== parent) {
-                                                       while (upperParentStart.parentNode !== parent) {
-                                                               upperParentStart = upperParentStart.parentNode;
-                                                       }
-                                               }
-                                               
-                                               var newElement = editor._doc.createElement(element);
-                                               newElement.innerHTML = range.htmlText;
-                                                       // IE eats spaces on the start boundary
-                                               if (range.htmlText.charAt(0) === "\x20") {
-                                                       newElement.innerHTML = "&nbsp;" + newElement.innerHTML;
-                                               }
-                                               var newElementClone = newElement.cloneNode(true);
-                                               range.pasteHTML(newElement.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() === element) {
-                                                       parent.insertBefore(newElementClone, upperParentStart.nextSibling);
-                                                       parentStart.removeChild(parentStart.lastChild);
-                                                               // Sometimes an empty previous sibling was created
-                                                       if (newElementClone.previousSibling
-                                                                       && newElementClone.previousSibling.nodeType === 1
-                                                                       && !newElementClone.previousSibling.innerText) {
-                                                               parent.removeChild(newElementClone.previousSibling);
-                                                       }
-                                                               // The bookmark will not work anymore
-                                                       newRange.moveToElementText(newElementClone);
-                                                       newRange.collapse(false);
-                                                       newRange.select();
-                                               } else {
-                                                               // Working around IE boookmark bug
-                                                       if (parentStart != parentEnd) {
-                                                               var newRange = editor._createRange();
-                                                               if (newRange.moveToBookmark(bookmark)) {
-                                                                       newRange.collapse(false);
-                                                                       newRange.select();
-                                                               }
-                                                       } else {
-                                                               range.collapse(false);
-                                                       }
-                                               }
-                                               try { // normalize() is not available in IE5.5
-                                                       parent.normalize();
-                                               } catch(e) { }
+                                               editor.wrapWithInlineElement(newElement, selection, range);
                                        }
                                }
                        } else {
@@ -359,7 +301,7 @@ InlineElements = HTMLArea.Plugin.extend({
                                        if (elementIsAncestor) {
                                                parent = ancestors[elementAncestorIndex];
                                        }
-                                       this.removeMarkup(parent);
+                                       editor.removeMarkup(parent);
                                }
                        }
                } else {
@@ -369,7 +311,7 @@ InlineElements = HTMLArea.Plugin.extend({
                                        if (elementIsAncestor) {
                                                parent = ancestors[elementAncestorIndex];
                                        }
-                                       this.removeMarkup(parent);
+                                       editor.removeMarkup(parent);
                                } else {
                                        var bookmark = this.editor.getBookmark(range);
                                        var newElement = this.remapMarkup(parent, element);
@@ -409,19 +351,6 @@ InlineElements = HTMLArea.Plugin.extend({
        },
        
        /*
-        * This function removes the given markup element
-        */
-       removeMarkup : function(element) {
-               var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
-               var parent = element.parentNode;
-               while (element.firstChild) {
-                       parent.insertBefore(element.firstChild, element);
-               }
-               parent.removeChild(element);
-               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
-       },
-       
-       /*
        * This function gets called when the toolbar is updated
        */
        onUpdateToolbar : function () {
@@ -440,7 +369,7 @@ InlineElements = HTMLArea.Plugin.extend({
                                var ancestors = editor.getAllAncestors();
                                for (var i = 0; i < ancestors.length; ++i) {
                                        fullNodeSelected = (editor._statusBarTree.selected === ancestors[i])
-                                               && ((HTMLArea.is_gecko && ancestors[i].textContent === range.toString()) || (HTMLArea.is_ie && ancestors[i].innerText === range.text));
+                                               && ((HTMLArea.is_gecko && ancestors[i].textContent === range.toString()) || (HTMLArea.is_ie && ((sel.type !== "Control" && ancestors[i].innerText === range.text) || (sel.type === "Control" && ancestors[i].innerText === range.item(0).text))));
                                        if (fullNodeSelected) {
                                                if (!HTMLArea.isBlockElement(ancestors[i])) {
                                                        tagName = ancestors[i].nodeName.toLowerCase();
@@ -455,7 +384,7 @@ InlineElements = HTMLArea.Plugin.extend({
                                }
                        }
                        var selectionInInlineElement = tagName && this.REInlineElements.test(tagName);
-                       var disabled = !this.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
+                       var disabled = !editor.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
                        
                        var obj = editor.config.customSelects["FormatText"];
                        if ((typeof(obj) !== "undefined") && (typeof(editor._toolbarObjects[obj.id]) !== "undefined")) {
@@ -485,20 +414,6 @@ InlineElements = HTMLArea.Plugin.extend({
        },
        
        /*
-        * This function determines if the end poins of the current selection are within the same block
-        */
-       endPointsInSameBlock : function() {
-               var selection = this.editor._getSelection();
-               if (this.editor._selectionEmpty(selection)) {
-                       return true;
-               } else {
-                       var parent = this.editor.getParentElement(selection);
-                       var endBlocks = this.editor.getEndBlocks(selection);
-                       return (endBlocks.start === endBlocks.end && !/^(table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
-               }
-       },
-       
-       /*
        * This function updates the drop-down list of inline elemenents
        */
        updateValue : function (editor, obj, tagName, selectionEmpty, fullNodeSelected, disabled) {
index 7ecf8e4..5a9fc86 100644 (file)
@@ -89,6 +89,9 @@ TextStyle = HTMLArea.Plugin.extend({
                 */
                this.REInlineTags = /^(abbr|acronym|b|bdo|big|cite|code|del|dfn|em|i|ins|kbd|q|samp|small|span|strike|strong|sub|sup|tt|u|var)$/;
                
+                       // Allowed attributes on inline elements
+               this.allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", (HTMLArea.is_gecko?"class":"className"));
+               
                /*
                 * Registering plugin "About" information
                 */
@@ -124,7 +127,18 @@ TextStyle = HTMLArea.Plugin.extend({
        isInlineElement : function (el) {
                return el && (el.nodeType === 1) && this.REInlineTags.test(el.nodeName.toLowerCase());
        },
-       
+
+       /*
+        * This function adds an attribute to the array of allowed attributes on inline elements
+        *
+        * @param       string  attribute: the name of the attribute to be added to the array
+        *
+        * @return      void
+        */
+       addAllowedAttribute : function (attribute) {
+               this.allowedAttributes.push(attribute);
+       },
+
        /*
         * This function gets called when some style in the drop-down list applies it to the highlighted textt
         */
@@ -166,80 +180,11 @@ TextStyle = HTMLArea.Plugin.extend({
                                // The selection is not empty, nor full element
                        if (className !== "none") {
                                        // Add span element with class attribute
+                               var newElement = editor._doc.createElement("span");
+                               HTMLArea._addClass(newElement, className);
+                               editor.wrapWithInlineElement(newElement, selection, range);
                                if (HTMLArea.is_gecko) {
-                                       var newElement = this.editor._doc.createElement("span");
-                                       HTMLArea._addClass(newElement, className);
-                                       range.surroundContents(newElement);
-                                       newElement.normalize();
-                                       parent.normalize();
-                                               // Firefox sometimes inserts empty elements just outside the boundaries of the range
-                                       var neighbour = newElement.previousSibling;
-                                       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-                                               HTMLArea.removeFromParent(neighbour);
-                                       }
-                                       neighbour = newElement.nextSibling;
-                                       if (neighbour && (neighbour.nodeType != 3) && !/\S/.test(neighbour.textContent)) {
-                                               HTMLArea.removeFromParent(neighbour);
-                                       }
-                                       this.editor.selectNodeContents(newElement, false);
                                        range.detach();
-                               } else {
-                                       var rangeStart = range.duplicate();
-                                       rangeStart.collapse(true);
-                                       var parentStart = rangeStart.parentElement();
-                                       var rangeEnd = range.duplicate();
-                                       rangeEnd.collapse(true);
-                                       var parentEnd = rangeEnd.parentElement();
-                                       var newRange = editor._createRange();
-                                       
-                                       var upperParentStart = parentStart;
-                                       if (parentStart !== parent) {
-                                               while (upperParentStart.parentNode !== parent) {
-                                                       upperParentStart = upperParentStart.parentNode;
-                                               }
-                                       }
-                                               
-                                       var newElement = editor._doc.createElement("span");
-                                       HTMLArea._addClass(newElement, className);
-                                       newElement.innerHTML = range.htmlText;
-                                               // IE eats spaces on the start boundary
-                                       if (range.htmlText.charAt(0) === "\x20") {
-                                               newElement.innerHTML = "&nbsp;" + newElement.innerHTML;
-                                       }
-                                       var newElementClone = newElement.cloneNode(true);
-                                       range.pasteHTML(newElement.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() === "span") {
-                                               parent.insertBefore(newElementClone, upperParentStart.nextSibling);
-                                               parentStart.removeChild(parentStart.lastChild);
-                                                       // Sometimes an empty previous sibling was created
-                                               if (newElementClone.previousSibling
-                                                               && newElementClone.previousSibling.nodeType === 1
-                                                               && !newElementClone.previousSibling.innerText) {
-                                                       parent.removeChild(newElementClone.previousSibling);
-                                               }
-                                                       // The bookmark will not work anymore
-                                               newRange.moveToElementText(newElementClone);
-                                               newRange.collapse(false);
-                                               newRange.select();
-                                       } else {
-                                                       // Working around IE boookmark bug
-                                               if (parentStart != parentEnd) {
-                                                       var newRange = editor._createRange();
-                                                       if (newRange.moveToBookmark(bookmark)) {
-                                                               newRange.collapse(false);
-                                                               newRange.select();
-                                                       }
-                                               } else {
-                                                       range.collapse(false);
-                                               }
-                                       }
-                                       try { // normalize() not available in IE5.5
-                                               parent.normalize();
-                                       } catch(e) { }
                                }
                        }
                } else {
@@ -253,39 +198,13 @@ TextStyle = HTMLArea.Plugin.extend({
                                        HTMLArea._addClass(parent, className);
                                }
                                        // Remove the span tag if it has no more attribute
-                               if ((parent.nodeName.toLowerCase() === "span") && !this.hasAllowedAttributes(parent)) {
-                                       this.removeMarkup(parent);
+                               if ((parent.nodeName.toLowerCase() === "span") && !HTMLArea.hasAllowedAttributes(parent, this.allowedAttributes)) {
+                                       editor.removeMarkup(parent);
                                }
                        }
                }
        },
-       
-       /*
-        * This function verifies if the element has any of the allowed attributes
-        */
-       hasAllowedAttributes : function(element) {
-               var allowedAttributes = new Array("id", "title", "lang", "xml:lang", "dir", "class", "className");
-               for (var i = 0; i < allowedAttributes.length; ++i) {
-                       if (element.getAttribute(allowedAttributes[i])) {
-                               return true;
-                       }
-               }
-               return false;
-       },
-       
-       /*
-        * This function removes the given markup element
-        */
-       removeMarkup : function(element) {
-               var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
-               var parent = element.parentNode;
-               while (element.firstChild) {
-                       parent.insertBefore(element.firstChild, element);
-               }
-               parent.removeChild(element);
-               this.editor.selectRange(this.editor.moveToBookmark(bookmark));
-       },
-       
+
        /*
         * This function gets called when the plugin is generated
         * Get the classes configuration and initiate the parsing of the style sheets
@@ -512,7 +431,7 @@ TextStyle = HTMLArea.Plugin.extend({
                                }
                        }
                        var selectionInInlineElement = tagName && this.REInlineTags.test(tagName);
-                       var disabled = !this.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
+                       var disabled = !editor.endPointsInSameBlock() || (fullNodeSelected && !tagName) || (selectionEmpty && !selectionInInlineElement);
                        if (!disabled && !tagName) {
                                tagName = "span";
                        }
@@ -520,21 +439,7 @@ TextStyle = HTMLArea.Plugin.extend({
                        this.updateValue(dropDownId, tagName, classNames, selectionEmpty, fullNodeSelected, disabled);
                }
        },
-       
-       /*
-        * This function determines if the end poins of the current selection are within the same block
-        */
-       endPointsInSameBlock : function() {
-               var selection = this.editor._getSelection();
-               if (this.editor._selectionEmpty(selection)) {
-                       return true;
-               } else {
-                       var parent = this.editor.getParentElement(selection);
-                       var endBlocks = this.editor.getEndBlocks(selection);
-                       return (endBlocks.start === endBlocks.end && !/^(body|table|thead|tbody|tfoot|tr)$/i.test(parent.nodeName));
-               }
-       },
-       
+
        updateValue : function(dropDownId, tagName, classNames, selectionEmpty, fullNodeSelected, disabled) {
                var editor = this.editor;
                var select = document.getElementById(editor._toolbarObjects[dropDownId]["elementId"]);