]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/TinyMCE/js/tiny_mce_src.js
Merge remote branch 'chat-interface-plugins/irc-plugin' into 1.0.x
[quix0rs-gnu-social.git] / plugins / TinyMCE / js / tiny_mce_src.js
index a26e4ea0047fc8ed431fadb917ac2d6f54ad6625..acc1db528ebe57464f843a1ab93db38f3b3ae74a 100644 (file)
@@ -2,12 +2,12 @@
        var whiteSpaceRe = /^\s*|\s*$/g,\r
                undefined;\r
 \r
-       win.tinymce = win.tinyMCE = {\r
+       var tinymce = {\r
                majorVersion : '3',\r
 \r
-               minorVersion : '3rc1',\r
+               minorVersion : '3.8',\r
 \r
-               releaseDate : '2010-02-23',\r
+               releaseDate : '2010-06-30',\r
 \r
                _init : function() {\r
                        var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
@@ -26,6 +26,8 @@
 \r
                        t.isAir = /adobeair/i.test(ua);\r
 \r
+                       t.isIDevice = /(iPad|iPhone)/.test(ua);\r
+\r
                        // TinyMCE .NET webcontrol might be setting the values for TinyMCE\r
                        if (win.tinyMCEPreInit) {\r
                                t.suffix = tinyMCEPreInit.suffix;\r
                createNS : function(n, o) {\r
                        var i, v;\r
 \r
-                       o = o || window;\r
+                       o = o || win;\r
 \r
                        n = n.split('.');\r
                        for (i=0; i<n.length; i++) {\r
 \r
        // Initialize the API\r
        tinymce._init();\r
+\r
+       // Expose tinymce namespace to the global namespace (window)\r
+       win.tinymce = win.tinyMCE = tinymce;\r
 })(window);\r
 \r
 (function($, tinymce) {\r
        tinymce.onCreate = function(ty, c, p) {\r
                tinymce.extend(p, patches[c]);\r
        };\r
-})(jQuery, tinymce);\r
+})(window.jQuery, tinymce);\r
+\r
+\r
 \r
 tinymce.create('tinymce.util.Dispatcher', {\r
        scope : null,\r
@@ -759,6 +766,7 @@ tinymce.create('tinymce.util.Dispatcher', {
        }\r
 \r
        });\r
+\r
 (function() {\r
        var each = tinymce.each;\r
 \r
@@ -999,6 +1007,7 @@ tinymce.create('tinymce.util.Dispatcher', {
                }\r
        });\r
 })();\r
+\r
 (function() {\r
        var each = tinymce.each;\r
 \r
@@ -1069,6 +1078,7 @@ tinymce.create('tinymce.util.Dispatcher', {
                }\r
        });\r
 })();\r
+\r
 tinymce.create('static tinymce.util.JSON', {\r
        serialize : function(o) {\r
                var i, v, s = tinymce.util.JSON.serialize, t;\r
@@ -1121,6 +1131,7 @@ tinymce.create('static tinymce.util.JSON', {
        }\r
 \r
        });\r
+\r
 tinymce.create('static tinymce.util.XHR', {\r
        send : function(o) {\r
                var x, t, w = window, c = 0;\r
@@ -1179,6 +1190,7 @@ tinymce.create('static tinymce.util.XHR', {
                }\r
        }\r
 });\r
+\r
 (function() {\r
        var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR;\r
 \r
@@ -1231,7 +1243,8 @@ tinymce.create('static tinymce.util.XHR', {
                        }\r
                }\r
        });\r
-}());(function(tinymce) {\r
+}());\r
+(function(tinymce) {\r
        // Shorten names\r
        var each = tinymce.each,\r
                is = tinymce.is,\r
@@ -1480,38 +1493,26 @@ tinymce.create('static tinymce.util.XHR', {
                        return o + ' />';\r
                },\r
 \r
-               remove : function(n, k) {\r
-                       var t = this;\r
-\r
-                       return this.run(n, function(n) {\r
-                               var p, g, i;\r
+               remove : function(node, keep_children) {\r
+                       return this.run(node, function(node) {\r
+                               var parent, child;\r
 \r
-                               p = n.parentNode;\r
+                               parent = node.parentNode;\r
 \r
-                               if (!p)\r
+                               if (!parent)\r
                                        return null;\r
 \r
-                               if (k) {\r
-                                       for (i = n.childNodes.length - 1; i >= 0; i--)\r
-                                               t.insertAfter(n.childNodes[i], n);\r
-\r
-                                       //each(n.childNodes, function(c) {\r
-                                       //      p.insertBefore(c.cloneNode(true), n);\r
-                                       //});\r
-                               }\r
-\r
-                               // Fix IE psuedo leak\r
-                               if (t.fixPsuedoLeaks) {\r
-                                       p = n.cloneNode(true);\r
-                                       k = 'IELeakGarbageBin';\r
-                                       g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'});\r
-                                       g.appendChild(n);\r
-                                       g.innerHTML = '';\r
-\r
-                                       return p;\r
+                               if (keep_children) {\r
+                                       while (child = node.firstChild) {\r
+                                               // IE 8 will crash if you don't remove completely empty text nodes\r
+                                               if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)\r
+                                                       parent.insertBefore(child, node);\r
+                                               else\r
+                                                       node.removeChild(child);\r
+                                       }\r
                                }\r
 \r
-                               return p.removeChild(n);\r
+                               return parent.removeChild(node);\r
                        });\r
                },\r
 \r
@@ -1818,7 +1819,6 @@ tinymce.create('static tinymce.util.XHR', {
                                        e = t.boxModel ? d.documentElement : d.body;\r
                                        x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border\r
                                        x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;\r
-                                       n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset\r
 \r
                                        return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};\r
                                }\r
@@ -2045,8 +2045,10 @@ tinymce.create('static tinymce.util.XHR', {
                                        e.className = v;\r
 \r
                                        // Empty class attr\r
-                                       if (!v)\r
+                                       if (!v) {\r
                                                e.removeAttribute('class');\r
+                                               e.removeAttribute('className');\r
+                                       }\r
 \r
                                        return v;\r
                                }\r
@@ -2148,7 +2150,7 @@ tinymce.create('static tinymce.util.XHR', {
                                                // So if we replace the p elements with divs and mark them and then replace them back to paragraphs\r
                                                // after we use innerHTML we can fix the DOM tree\r
                                                h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');\r
-                                               h = h.replace(/<\/p>/g, '</div>');\r
+                                               h = h.replace(/<\/p>/gi, '</div>');\r
 \r
                                                // Set the new HTML with DIVs\r
                                                set();\r
@@ -2405,23 +2407,21 @@ tinymce.create('static tinymce.util.XHR', {
                        });\r
                },\r
 \r
-               insertAfter : function(n, r) {\r
-                       var t = this;\r
-\r
-                       r = t.get(r);\r
+               insertAfter : function(node, reference_node) {\r
+                       reference_node = this.get(reference_node);\r
 \r
-                       return this.run(n, function(n) {\r
-                               var p, ns;\r
+                       return this.run(node, function(node) {\r
+                               var parent, nextSibling;\r
 \r
-                               p = r.parentNode;\r
-                               ns = r.nextSibling;\r
+                               parent = reference_node.parentNode;\r
+                               nextSibling = reference_node.nextSibling;\r
 \r
-                               if (ns)\r
-                                       p.insertBefore(n, ns);\r
+                               if (nextSibling)\r
+                                       parent.insertBefore(node, nextSibling);\r
                                else\r
-                                       p.appendChild(n);\r
+                                       parent.appendChild(node);\r
 \r
-                               return n;\r
+                               return node;\r
                        });\r
                },\r
 \r
@@ -2447,14 +2447,6 @@ tinymce.create('static tinymce.util.XHR', {
                                        });\r
                                }\r
 \r
-                               // Fix IE psuedo leak for elements since replacing elements if fairly common\r
-                               // Will break parentNode for some unknown reason\r
-                               if (t.fixPsuedoLeaks && o.nodeType === 1) {\r
-                                       o.parentNode.insertBefore(n, o);\r
-                                       t.remove(o);\r
-                                       return n;\r
-                               }\r
-\r
                                return o.parentNode.replaceChild(n, o);\r
                        });\r
                },\r
@@ -2656,21 +2648,20 @@ tinymce.create('static tinymce.util.XHR', {
                },\r
 \r
                nodeIndex : function(node, normalized) {\r
-                       var idx = 0, lastNode, nodeType;\r
+                       var idx = 0, lastNodeType, lastNode, nodeType;\r
 \r
                        if (node) {\r
-                               for (node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
+                               for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {\r
                                        nodeType = node.nodeType;\r
 \r
-                                       // Text nodes needs special treatment if the normalized argument is specified\r
+                                       // Normalize text nodes\r
                                        if (normalized && nodeType == 3) {\r
-                                               // Checks if the current node has contents and that the last node is a non text node or empty\r
-                                               if (node.nodeValue.length > 0 && (lastNode.nodeType != nodeType || lastNode.nodeValue.length === 0))\r
-                                                       idx++;\r
-                                       } else\r
-                                               idx++;\r
+                                               if (nodeType == lastNodeType || !node.nodeValue.length)\r
+                                                       continue;\r
+                                       }\r
 \r
-                                       lastNode = node;\r
+                                       idx++;\r
+                                       lastNodeType = nodeType;\r
                                }\r
                        }\r
 \r
@@ -2827,6 +2818,7 @@ tinymce.create('static tinymce.util.XHR', {
 \r
        tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0});\r
 })(tinymce);\r
+\r
 (function(ns) {\r
        // Range constructor\r
        function Range(dom) {\r
@@ -3503,39 +3495,14 @@ tinymce.create('static tinymce.util.XHR', {
 \r
        ns.Range = Range;\r
 })(tinymce.dom);\r
+\r
 (function() {\r
        function Selection(selection) {\r
                var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false;\r
 \r
-               // Compares two IE specific ranges to see if they are different\r
-               // this method is useful when invalidating the cached selection range\r
-               function compareRanges(rng1, rng2) {\r
-                       if (rng1 && rng2) {\r
-                               // Both are control ranges and the selected element matches\r
-                               if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
-                                       return TRUE;\r
-\r
-                               // Both are text ranges and the range matches\r
-                               if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {\r
-                                       // IE will say that the range is equal then produce an invalid argument exception\r
-                                       // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
-                                       // This hack will invalidate the range cache if the exception occurs\r
-                                       try {\r
-                                               // Try accessing nextSibling will producer an invalid argument some times\r
-                                               range.startContainer.nextSibling;\r
-                                               return TRUE;\r
-                                       } catch (ex) {\r
-                                               // Ignore\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       return FALSE;\r
-               };\r
-\r
                // Returns a W3C DOM compatible range object by using the IE Range API\r
                function getRange() {\r
-                       var ieRange = selection.getRng(), domRange = dom.createRng(), ieRange2, element, collapsed, isMerged;\r
+                       var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed;\r
 \r
                        // If selection is outside the current document just return an empty range\r
                        element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();\r
@@ -3550,233 +3517,199 @@ tinymce.create('static tinymce.util.XHR', {
                                return domRange;\r
                        }\r
 \r
-                       // Duplicare IE selection range and check if the range is collapsed\r
-                       ieRange2 = ieRange.duplicate();\r
                        collapsed = selection.isCollapsed();\r
 \r
-                       // Insert invisible start marker\r
-                       ieRange.collapse();\r
-                       ieRange.pasteHTML('<span id="_mce_start" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
+                       function findEndPoint(start) {\r
+                               var marker, container, offset, nodes, startIndex = 0, endIndex, index, parent, checkRng, position;\r
 \r
-                       // Insert invisible end marker\r
-                       if (!collapsed) {\r
-                               ieRange2.collapse(FALSE);\r
-                               ieRange2.pasteHTML('<span id="_mce_end" style="display:none;line-height:0">' + invisibleChar + '</span>');\r
-                       }\r
+                               // Setup temp range and collapse it\r
+                               checkRng = ieRange.duplicate();\r
+                               checkRng.collapse(start);\r
 \r
-                       // Sets the end point of the range by looking for the marker\r
-                       // This method also merges the text nodes it splits so that\r
-                       // the DOM doesn't get fragmented.\r
-                       function setEndPoint(start) {\r
-                               var container, offset, marker, sibling;\r
+                               // Create marker and insert it at the end of the endpoints parent\r
+                               marker = dom.create('a');\r
+                               parent = checkRng.parentElement();\r
 \r
-                               // Look for endpoint marker\r
-                               marker = dom.get('_mce_' + (start ? 'start' : 'end'));\r
-                               sibling = marker.previousSibling;\r
+                               // If parent doesn't have any children then set the container to that parent and the index to 0\r
+                               if (!parent.hasChildNodes()) {\r
+                                       domRange[start ? 'setStart' : 'setEnd'](parent, 0);\r
+                                       return;\r
+                               }\r
 \r
-                               // Is marker after a text node\r
-                               if (sibling && sibling.nodeType == 3) {\r
-                                       // Get container node and calc offset\r
-                                       container = sibling;\r
-                                       offset = container.nodeValue.length;\r
+                               parent.appendChild(marker);\r
+                               checkRng.moveToElementText(marker);\r
+                               position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
+                               if (position > 0) {\r
+                                       // The position is after the end of the parent element.\r
+                                       // This is the case where IE puts the caret to the left edge of a table.\r
+                                       domRange[start ? 'setStartAfter' : 'setEndAfter'](parent);\r
                                        dom.remove(marker);\r
+                                       return;\r
+                               }\r
 \r
-                                       // Merge text nodes to reduce DOM fragmentation\r
-                                       sibling = container.nextSibling;\r
-                                       if (sibling && sibling.nodeType == 3) {\r
-                                               isMerged = TRUE;\r
-                                               container.appendData(sibling.nodeValue);\r
-                                               dom.remove(sibling);\r
+                               // Setup node list and endIndex\r
+                               nodes = tinymce.grep(parent.childNodes);\r
+                               endIndex = nodes.length - 1;\r
+                               // Perform a binary search for the position\r
+                               while (startIndex <= endIndex) {\r
+                                       index = Math.floor((startIndex + endIndex) / 2);\r
+\r
+                                       // Insert marker and check it's position relative to the selection\r
+                                       parent.insertBefore(marker, nodes[index]);\r
+                                       checkRng.moveToElementText(marker);\r
+                                       position = ieRange.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', checkRng);\r
+                                       if (position > 0) {\r
+                                               // Marker is to the right\r
+                                               startIndex = index + 1;\r
+                                       } else if (position < 0) {\r
+                                               // Marker is to the left\r
+                                               endIndex = index - 1;\r
+                                       } else {\r
+                                               // Maker is where we are\r
+                                               found = true;\r
+                                               break;\r
                                        }\r
-                               } else {\r
-                                       sibling = marker.nextSibling;\r
+                               }\r
 \r
-                                       // Is marker before a text node\r
-                                       if (sibling && sibling.nodeType == 3) {\r
-                                               container = sibling;\r
-                                               offset = 0;\r
-                                       } else {\r
-                                               // Is marker before an element\r
-                                               if (sibling)\r
-                                                       offset = dom.nodeIndex(sibling) - 1;\r
-                                               else\r
-                                                       offset = dom.nodeIndex(marker);\r
+                               // Setup container\r
+                               container = position > 0 || index == 0 ? marker.nextSibling : marker.previousSibling;\r
+\r
+                               // Handle element selection\r
+                               if (container.nodeType == 1) {\r
+                                       dom.remove(marker);\r
+\r
+                                       // Find offset and container\r
+                                       offset = dom.nodeIndex(container);\r
+                                       container = container.parentNode;\r
 \r
-                                               container = marker.parentNode;\r
+                                       // Move the offset if we are setting the end or the position is after an element\r
+                                       if (!start || index > 0)\r
+                                               offset++;\r
+                               } else {\r
+                                       // Calculate offset within text node\r
+                                       if (position > 0 || index == 0) {\r
+                                               checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
+                                               offset = checkRng.text.length;\r
+                                       } else {\r
+                                               checkRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', ieRange);\r
+                                               offset = container.nodeValue.length - checkRng.text.length;\r
                                        }\r
 \r
                                        dom.remove(marker);\r
                                }\r
 \r
-                               // Set start of range\r
-                               if (start)\r
-                                       domRange.setStart(container, offset);\r
-\r
-                               // Set end of range or automatically if it's collapsed to increase performance\r
-                               if (!start || collapsed)\r
-                                       domRange.setEnd(container, offset);\r
+                               domRange[start ? 'setStart' : 'setEnd'](container, offset);\r
                        };\r
 \r
-                       // Set start of range\r
-                       setEndPoint(TRUE);\r
+                       // Find start point\r
+                       findEndPoint(true);\r
 \r
-                       // Set end of range if needed\r
+                       // Find end point if needed\r
                        if (!collapsed)\r
-                               setEndPoint(FALSE);\r
-\r
-                       // Restore selection if the range contents was merged\r
-                       // since the selection was then moved since the text nodes got changed\r
-                       if (isMerged)\r
-                               t.addRange(domRange);\r
+                               findEndPoint();\r
 \r
                        return domRange;\r
                };\r
 \r
                this.addRange = function(rng) {\r
-                       var ieRng, ieRng2, doc = selection.dom.doc, body = doc.body, startPos, endPos, sc, so, ec, eo, marker, lastIndex, skipStart, skipEnd;\r
-\r
-                       this.destroy();\r
-\r
-                       // Setup some shorter versions\r
-                       sc = rng.startContainer;\r
-                       so = rng.startOffset;\r
-                       ec = rng.endContainer;\r
-                       eo = rng.endOffset;\r
-                       ieRng = body.createTextRange();\r
-\r
-                       // If document selection move caret to first node in document\r
-                       if (sc == doc || ec == doc) {\r
-                               ieRng = body.createTextRange();\r
-                               ieRng.collapse();\r
-                               ieRng.select();\r
-                               return;\r
-                       }\r
-\r
-                       // If child index resolve it\r
-                       if (sc.nodeType == 1 && sc.hasChildNodes()) {\r
-                               lastIndex = sc.childNodes.length - 1;\r
+                       var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;\r
 \r
-                               // Index is higher that the child count then we need to jump over the start container\r
-                               if (so > lastIndex) {\r
-                                       skipStart = 1;\r
-                                       sc = sc.childNodes[lastIndex];\r
-                               } else\r
-                                       sc = sc.childNodes[so];\r
-\r
-                               // Child was text node then move offset to start of it\r
-                               if (sc.nodeType == 3)\r
-                                       so = 0;\r
-                       }\r
-\r
-                       // If child index resolve it\r
-                       if (ec.nodeType == 1 && ec.hasChildNodes()) {\r
-                               lastIndex = ec.childNodes.length - 1;\r
+                       function setEndPoint(start) {\r
+                               var container, offset, marker, tmpRng, nodes;\r
 \r
-                               if (eo == 0) {\r
-                                       skipEnd = 1;\r
-                                       ec = ec.childNodes[0];\r
-                               } else {\r
-                                       ec = ec.childNodes[Math.min(lastIndex, eo - 1)];\r
+                               marker = dom.create('a');\r
+                               container = start ? startContainer : endContainer;\r
+                               offset = start ? startOffset : endOffset;\r
+                               tmpRng = ieRng.duplicate();\r
 \r
-                                       // Child was text node then move offset to end of text node\r
-                                       if (ec.nodeType == 3)\r
-                                               eo = ec.nodeValue.length;\r
+                               if (container == doc) {\r
+                                       container = body;\r
+                                       offset = 0;\r
                                }\r
-                       }\r
 \r
-                       // Single element selection\r
-                       if (sc == ec && sc.nodeType == 1) {\r
-                               // Make control selection for some elements\r
-                               if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) {\r
-                                       ieRng = body.createControlRange();\r
-                                       ieRng.addElement(sc);\r
+                               if (container.nodeType == 3) {\r
+                                       container.parentNode.insertBefore(marker, container);\r
+                                       tmpRng.moveToElementText(marker);\r
+                                       tmpRng.moveStart('character', offset);\r
+                                       dom.remove(marker);\r
+                                       ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
                                } else {\r
-                                       ieRng = body.createTextRange();\r
+                                       nodes = container.childNodes;\r
 \r
-                                       // Padd empty elements with invisible character\r
-                                       if (!sc.hasChildNodes() && sc.canHaveHTML)\r
-                                               sc.innerHTML = invisibleChar;\r
-\r
-                                       // Select element contents\r
-                                       ieRng.moveToElementText(sc);\r
+                                       if (nodes.length) {\r
+                                               if (offset >= nodes.length) {\r
+                                                       dom.insertAfter(marker, nodes[nodes.length - 1]);\r
+                                               } else {\r
+                                                       container.insertBefore(marker, nodes[offset]);\r
+                                               }\r
 \r
-                                       // If it's only containing a padding remove it so the caret remains\r
-                                       if (sc.innerHTML == invisibleChar) {\r
-                                               ieRng.collapse(TRUE);\r
-                                               sc.removeChild(sc.firstChild);\r
+                                               tmpRng.moveToElementText(marker);\r
+                                       } else {\r
+                                               // Empty node selection for example <div>|</div>\r
+                                               marker = doc.createTextNode(invisibleChar);\r
+                                               container.appendChild(marker);\r
+                                               tmpRng.moveToElementText(marker.parentNode);\r
+                                               tmpRng.collapse(TRUE);\r
                                        }\r
-                               }\r
-\r
-                               if (so == eo)\r
-                                       ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1);\r
 \r
-                               ieRng.select();\r
-                               ieRng.scrollIntoView();\r
-                               return;\r
+                                       ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);\r
+                                       dom.remove(marker);\r
+                               }\r
                        }\r
 \r
-                       // Create range and marker\r
-                       ieRng = body.createTextRange();\r
-                       marker = doc.createElement('span');\r
-                       marker.innerHTML = ' ';\r
-\r
-                       // Set start of range to startContainer/startOffset\r
-                       if (sc.nodeType == 3) {\r
-                               // Insert marker after/before startContainer\r
-                               if (skipStart)\r
-                                       dom.insertAfter(marker, sc);\r
-                               else\r
-                                       sc.parentNode.insertBefore(marker, sc);\r
-\r
-                               // Select marker the caret to offset position\r
-                               ieRng.moveToElementText(marker);\r
-                               marker.parentNode.removeChild(marker);\r
-                               ieRng.move('character', so);\r
-                       } else {\r
-                               ieRng.moveToElementText(sc);\r
+                       // Destroy cached range\r
+                       this.destroy();\r
 \r
-                               if (skipStart)\r
-                                       ieRng.collapse(FALSE);\r
-                       }\r
+                       // Setup some shorter versions\r
+                       startContainer = rng.startContainer;\r
+                       startOffset = rng.startOffset;\r
+                       endContainer = rng.endContainer;\r
+                       endOffset = rng.endOffset;\r
+                       ieRng = body.createTextRange();\r
 \r
-                       // If same text container then we can do a more simple move\r
-                       if (sc == ec && sc.nodeType == 3) {\r
-                               ieRng.moveEnd('character', eo - so);\r
-                               ieRng.select();\r
-                               ieRng.scrollIntoView();\r
-                               return;\r
+                       // If single element selection then try making a control selection out of it\r
+                       if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {\r
+                               if (startOffset == endOffset - 1) {\r
+                                       try {\r
+                                               ctrlRng = body.createControlRange();\r
+                                               ctrlRng.addElement(startContainer.childNodes[startOffset]);\r
+                                               ctrlRng.select();\r
+                                               ctrlRng.scrollIntoView();\r
+                                               return;\r
+                                       } catch (ex) {\r
+                                               // Ignore\r
+                                       }\r
+                               }\r
                        }\r
 \r
-                       // Set end of range to endContainer/endOffset\r
-                       ieRng2 = body.createTextRange();\r
-                       if (ec.nodeType == 3) {\r
-                               // Insert marker after/before startContainer\r
-                               ec.parentNode.insertBefore(marker, ec);\r
-\r
-                               // Move selection to end marker and move caret to end offset\r
-                               ieRng2.moveToElementText(marker);\r
-                               marker.parentNode.removeChild(marker);\r
-                               ieRng2.move('character', eo);\r
-                               ieRng.setEndPoint('EndToStart', ieRng2);\r
-                       } else {\r
-                               ieRng2.moveToElementText(ec);\r
-                               ieRng2.collapse(!!skipEnd);\r
-                               ieRng.setEndPoint('EndToEnd', ieRng2);\r
-                       }\r
+                       // Set start/end point of selection\r
+                       setEndPoint(true);\r
+                       setEndPoint();\r
 \r
+                       // Select the new range and scroll it into view\r
                        ieRng.select();\r
                        ieRng.scrollIntoView();\r
                };\r
 \r
                this.getRangeAt = function() {\r
                        // Setup new range if the cache is empty\r
-                       if (!range || !compareRanges(lastIERng, selection.getRng())) {\r
+                       if (!range || !tinymce.dom.RangeUtils.compareRanges(lastIERng, selection.getRng())) {\r
                                range = getRange();\r
 \r
                                // Store away text range for next call\r
                                lastIERng = selection.getRng();\r
                        }\r
 \r
+                       // IE will say that the range is equal then produce an invalid argument exception\r
+                       // if you perform specific operations in a keyup event. For example Ctrl+Del.\r
+                       // This hack will invalidate the range cache if the exception occurs\r
+                       try {\r
+                               range.startContainer.nextSibling;\r
+                       } catch (ex) {\r
+                               range = getRange();\r
+                               lastIERng = null;\r
+                       }\r
+\r
                        // Return cached range\r
                        return range;\r
                };\r
@@ -3863,6 +3796,8 @@ tinymce.create('static tinymce.util.XHR', {
        // Expose the selection object\r
        tinymce.dom.TridentSelection = Selection;\r
 })();\r
+\r
+\r
 (function(tinymce) {\r
        // Shorten names\r
        var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;\r
@@ -4146,6 +4081,7 @@ tinymce.create('static tinymce.util.XHR', {
                Event.destroy();\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        tinymce.dom.Element = function(id, settings) {\r
                var t = this, dom, el;\r
@@ -4254,6 +4190,7 @@ tinymce.create('static tinymce.util.XHR', {
                });\r
        };\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        function trimNl(s) {\r
                return s.replace(/[\n\r]+/g, '');\r
@@ -4341,17 +4278,21 @@ tinymce.create('static tinymce.util.XHR', {
                                h += '<span id="__caret">_</span>';\r
 \r
                                // Delete and insert new node\r
-                               if (r.startContainer == d && r.endContainer == d) {\r
+                               \r
+                               if (r.startContainer == d && r.endContainer ==  d) {\r
                                        // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents\r
                                        d.body.innerHTML = h;\r
                                } else {\r
                                        r.deleteContents();\r
-                                       r.insertNode(t.getRng().createContextualFragment(h));\r
+                                       if (d.body.childNodes.length == 0) {\r
+                                               d.body.innerHTML = h;\r
+                                       } else {\r
+                                               r.insertNode(r.createContextualFragment(h));\r
+                                       }\r
                                }\r
 \r
                                // Move to caret marker\r
                                c = t.dom.get('__caret');\r
-\r
                                // Make sure we wrap it compleatly, Opera fails with a simple select call\r
                                r = d.createRange();\r
                                r.setStartBefore(c);\r
@@ -4375,37 +4316,50 @@ tinymce.create('static tinymce.util.XHR', {
                },\r
 \r
                getStart : function() {\r
-                       var t = this, r = t.getRng(), e;\r
-\r
-                       if (isIE) {\r
-                               if (r.item)\r
-                                       return r.item(0);\r
+                       var rng = this.getRng(), startElement, parentElement, checkRng, node;\r
 \r
-                               r = r.duplicate();\r
-                               r.collapse(1);\r
-                               e = r.parentElement();\r
+                       if (rng.duplicate || rng.item) {\r
+                               // Control selection, return first item\r
+                               if (rng.item)\r
+                                       return rng.item(0);\r
+\r
+                               // Get start element\r
+                               checkRng = rng.duplicate();\r
+                               checkRng.collapse(1);\r
+                               startElement = checkRng.parentElement();\r
+\r
+                               // Check if range parent is inside the start element, then return the inner parent element\r
+                               // This will fix issues when a single element is selected, IE would otherwise return the wrong start element\r
+                               parentElement = node = rng.parentElement();\r
+                               while (node = node.parentNode) {\r
+                                       if (node == startElement) {\r
+                                               startElement = parentElement;\r
+                                               break;\r
+                                       }\r
+                               }\r
 \r
-                               if (e && e.nodeName == 'BODY')\r
-                                       return e.firstChild || e;\r
+                               // If start element is body element try to move to the first child if it exists\r
+                               if (startElement && startElement.nodeName == 'BODY')\r
+                                       return startElement.firstChild || startElement;\r
 \r
-                               return e;\r
+                               return startElement;\r
                        } else {\r
-                               e = r.startContainer;\r
+                               startElement = rng.startContainer;\r
 \r
-                               if (e.nodeType == 1 && e.hasChildNodes())\r
-                                       e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)];\r
+                               if (startElement.nodeType == 1 && startElement.hasChildNodes())\r
+                                       startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];\r
 \r
-                               if (e && e.nodeType == 3)\r
-                                       return e.parentNode;\r
+                               if (startElement && startElement.nodeType == 3)\r
+                                       return startElement.parentNode;\r
 \r
-                               return e;\r
+                               return startElement;\r
                        }\r
                },\r
 \r
                getEnd : function() {\r
                        var t = this, r = t.getRng(), e, eo;\r
 \r
-                       if (isIE) {\r
+                       if (r.duplicate || r.item) {\r
                                if (r.item)\r
                                        return r.item(0);\r
 \r
@@ -4450,23 +4404,8 @@ tinymce.create('static tinymce.util.XHR', {
                                        var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};\r
 \r
                                        function getPoint(rng, start) {\r
-                                               var indexes = [], node, lastIdx,\r
-                                                       container = rng[start ? 'startContainer' : 'endContainer'],\r
-                                                       offset = rng[start ? 'startOffset' : 'endOffset'], exclude, point = {};\r
-\r
-                                               // Resolve element index\r
-                                               if (container.nodeType == 1 && container.hasChildNodes()) {\r
-                                                       lastIdx = container.childNodes.length - 1;\r
-                                                       point.exclude = (start && offset > lastIdx) || (!start && offset == 0);\r
-\r
-                                                       if (!start && offset)\r
-                                                               offset--;\r
-\r
-                                                       container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
-\r
-                                                       if (container.nodeType == 3)\r
-                                                               offset = start ? 0 : container.nodeValue.length;\r
-                                               }\r
+                                               var container = rng[start ? 'startContainer' : 'endContainer'],\r
+                                                       offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;\r
 \r
                                                if (container.nodeType == 3) {\r
                                                        if (normalized) {\r
@@ -4474,13 +4413,20 @@ tinymce.create('static tinymce.util.XHR', {
                                                                        offset += node.nodeValue.length;\r
                                                        }\r
 \r
-                                                       point.offset = offset;\r
+                                                       point.push(offset);\r
+                                               } else {\r
+                                                       childNodes = container.childNodes;\r
+\r
+                                                       if (offset >= childNodes.length && childNodes.length) {\r
+                                                               after = 1;\r
+                                                               offset = Math.max(0, childNodes.length - 1);\r
+                                                       }\r
+\r
+                                                       point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);\r
                                                }\r
 \r
                                                for (; container && container != root; container = container.parentNode)\r
-                                                       indexes.push(t.dom.nodeIndex(container, normalized));\r
-\r
-                                               point.indexes = indexes;\r
+                                                       point.push(t.dom.nodeIndex(container, normalized));\r
 \r
                                                return point;\r
                                        };\r
@@ -4552,7 +4498,7 @@ tinymce.create('static tinymce.util.XHR', {
                },\r
 \r
                moveToBookmark : function(bookmark) {\r
-                       var t = this, dom = t.dom, marker1, marker2, rng, root;\r
+                       var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;\r
 \r
                        // Clear selection cache\r
                        if (t.tridentSel)\r
@@ -4564,31 +4510,22 @@ tinymce.create('static tinymce.util.XHR', {
                                        root = dom.getRoot();\r
 \r
                                        function setEndPoint(start) {\r
-                                               var point = bookmark[start ? 'start' : 'end'], i, node, offset;\r
+                                               var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;\r
 \r
                                                if (point) {\r
-                                                       for (node = root, i = point.indexes.length - 1; i >= 0; i--)\r
-                                                               node = node.childNodes[point.indexes[i]] || node;\r
-\r
-                                                       if (start) {\r
-                                                               if (node.nodeType == 3 && point.offset)\r
-                                                                       rng.setStart(node, point.offset);\r
-                                                               else {\r
-                                                                       if (point.exclude)\r
-                                                                               rng.setStartAfter(node);\r
-                                                                       else\r
-                                                                               rng.setStartBefore(node);\r
-                                                               }\r
-                                                       } else {\r
-                                                               if (node.nodeType == 3 && point.offset)\r
-                                                                       rng.setEnd(node, point.offset);\r
-                                                               else {\r
-                                                                       if (point.exclude)\r
-                                                                               rng.setEndBefore(node);\r
-                                                                       else\r
-                                                                               rng.setEndAfter(node);\r
-                                                               }\r
+                                                       // Find container node\r
+                                                       for (node = root, i = point.length - 1; i >= 1; i--) {\r
+                                                               children = node.childNodes;\r
+\r
+                                                               if (children.length)\r
+                                                                       node = children[point[i]];\r
                                                        }\r
+\r
+                                                       // Set offset within container node\r
+                                                       if (start)\r
+                                                               rng.setStart(node, point[0]);\r
+                                                       else\r
+                                                               rng.setEnd(node, point[0]);\r
                                                }\r
                                        };\r
 \r
@@ -4597,8 +4534,6 @@ tinymce.create('static tinymce.util.XHR', {
 \r
                                        t.setRng(rng);\r
                                } else if (bookmark.id) {\r
-                                       rng = dom.createRng();\r
-\r
                                        function restoreEndPoint(suffix) {\r
                                                var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;\r
 \r
@@ -4608,25 +4543,23 @@ tinymce.create('static tinymce.util.XHR', {
                                                        if (suffix == 'start') {\r
                                                                if (!keep) {\r
                                                                        idx = dom.nodeIndex(marker);\r
-\r
-                                                                       if (idx > 0)\r
-                                                                               idx++;\r
                                                                } else {\r
-                                                                       node = marker;\r
+                                                                       node = marker.firstChild;\r
                                                                        idx = 1;\r
                                                                }\r
 \r
-                                                               rng.setStart(node, idx);\r
-                                                               rng.setEnd(node, idx);\r
+                                                               startContainer = endContainer = node;\r
+                                                               startOffset = endOffset = idx;\r
                                                        } else {\r
                                                                if (!keep) {\r
                                                                        idx = dom.nodeIndex(marker);\r
                                                                } else {\r
-                                                                       node = marker;\r
+                                                                       node = marker.firstChild;\r
                                                                        idx = 1;\r
                                                                }\r
 \r
-                                                               rng.setEnd(node, idx);\r
+                                                               endContainer = node;\r
+                                                               endOffset = idx;\r
                                                        }\r
 \r
                                                        if (!keep) {\r
@@ -4651,19 +4584,33 @@ tinymce.create('static tinymce.util.XHR', {
                                                                        dom.remove(next);\r
 \r
                                                                        if (suffix == 'start') {\r
-                                                                               rng.setStart(prev, idx);\r
-                                                                               rng.setEnd(prev, idx);\r
-                                                                       } else\r
-                                                                               rng.setEnd(prev, idx);\r
+                                                                               startContainer = endContainer = prev;\r
+                                                                               startOffset = endOffset = idx;\r
+                                                                       } else {\r
+                                                                               endContainer = prev;\r
+                                                                               endOffset = idx;\r
+                                                                       }\r
                                                                }\r
                                                        }\r
                                                }\r
                                        };\r
 \r
+                                       function addBogus(node) {\r
+                                               // Adds a bogus BR element for empty block elements\r
+                                               // on non IE browsers just to have a place to put the caret\r
+                                               if (!isIE && dom.isBlock(node) && !node.innerHTML)\r
+                                                       node.innerHTML = '<br _mce_bogus="1" />';\r
+\r
+                                               return node;\r
+                                       };\r
+\r
                                        // Restore start/end points\r
                                        restoreEndPoint('start');\r
                                        restoreEndPoint('end');\r
 \r
+                                       rng = dom.createRng();\r
+                                       rng.setStart(addBogus(startContainer), startOffset);\r
+                                       rng.setEnd(addBogus(endContainer), endOffset);\r
                                        t.setRng(rng);\r
                                } else if (bookmark.name) {\r
                                        t.select(dom.select(bookmark.name)[bookmark.index]);\r
@@ -4766,20 +4713,32 @@ tinymce.create('static tinymce.util.XHR', {
                        // This can occur when the editor is placed in a hidden container element on Gecko\r
                        // Or on IE when there was an exception\r
                        if (!r)\r
-                               r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange();\r
+                               r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();\r
 \r
+                       if (t.selectedRange && t.explicitRange) {\r
+                               if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {\r
+                                       // Safari, Opera and Chrome only ever select text which causes the range to change.\r
+                                       // This lets us use the originally set range if the selection hasn't been changed by the user.\r
+                                       r = t.explicitRange;\r
+                               } else {\r
+                                       t.selectedRange = null;\r
+                                       t.explicitRange = null;\r
+                               }\r
+                       }\r
                        return r;\r
                },\r
 \r
                setRng : function(r) {\r
                        var s, t = this;\r
-\r
+                       \r
                        if (!t.tridentSel) {\r
                                s = t.getSel();\r
 \r
                                if (s) {\r
+                                       t.explicitRange = r;\r
                                        s.removeAllRanges();\r
                                        s.addRange(r);\r
+                                       t.selectedRange = s.getRangeAt(0);\r
                                }\r
                        } else {\r
                                // Is W3C Range\r
@@ -4808,7 +4767,7 @@ tinymce.create('static tinymce.util.XHR', {
                getNode : function() {\r
                        var t = this, rng = t.getRng(), sel = t.getSel(), elm;\r
 \r
-                       if (!isIE) {\r
+                       if (rng.setStart) {\r
                                // Range maybe lost after the editor is made visible again\r
                                if (!rng)\r
                                        return t.dom.getRoot();\r
@@ -4876,6 +4835,7 @@ tinymce.create('static tinymce.util.XHR', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        tinymce.create('tinymce.dom.XMLWriter', {\r
                node : null,\r
@@ -4967,6 +4927,7 @@ tinymce.create('static tinymce.util.XHR', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        tinymce.create('tinymce.dom.StringWriter', {\r
                str : null,\r
@@ -5093,6 +5054,7 @@ tinymce.create('static tinymce.util.XHR', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Shorten names\r
        var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko;\r
@@ -5615,7 +5577,7 @@ tinymce.create('static tinymce.util.XHR', {
                },\r
 \r
                _serializeNode : function(n, inner) {\r
-                       var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type;\r
+                       var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type, scopeName;\r
 \r
                        if (!s.node_filter || s.node_filter(n)) {\r
                                switch (n.nodeType) {\r
@@ -5639,8 +5601,9 @@ tinymce.create('static tinymce.util.XHR', {
 \r
                                                // Add correct prefix on IE\r
                                                if (isIE) {\r
-                                                       if (n.scopeName !== 'HTML' && n.scopeName !== 'html')\r
-                                                               nn = n.scopeName + ':' + nn;\r
+                                                       scopeName = n.scopeName;\r
+                                                       if (scopeName && scopeName !== 'HTML' && scopeName !== 'html')\r
+                                                               nn = scopeName + ':' + nn;\r
                                                }\r
 \r
                                                // Remove mce prefix on IE needed for the abbr element\r
@@ -5684,6 +5647,13 @@ tinymce.create('static tinymce.util.XHR', {
                                                }\r
 \r
                                                ru = t.findRule(nn);\r
+                                               \r
+                                               // No valid rule for this element could be found then skip it\r
+                                               if (!ru) {\r
+                                                       iv = true;\r
+                                                       break;\r
+                                               }\r
+\r
                                                nn = ru.name || nn;\r
                                                closed = s.closed.test(nn);\r
 \r
@@ -5959,6 +5929,7 @@ tinymce.create('static tinymce.util.XHR', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        tinymce.dom.ScriptLoader = function(settings) {\r
                var QUEUED = 0,\r
@@ -6142,6 +6113,7 @@ tinymce.create('static tinymce.util.XHR', {
        // Global script loader\r
        tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();\r
 })(tinymce);\r
+\r
 tinymce.dom.TreeWalker = function(start_node, root_node) {\r
        var node = start_node;\r
 \r
@@ -6181,6 +6153,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                return (node = findSibling(node, 'lastChild', 'lastSibling', shallow));\r
        };\r
 };\r
+\r
 (function() {\r
        var transitional = {};\r
 \r
@@ -6334,7 +6307,8 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                        return !!(element && (!child_name || element[child_name]));\r
                };\r
        };\r
-})();(function(tinymce) {\r
+})();\r
+(function(tinymce) {\r
        tinymce.dom.RangeUtils = function(dom) {\r
                var INVISIBLE_CHAR = '\uFEFF';\r
 \r
@@ -6495,7 +6469,28 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                };\r
 */\r
        };\r
+\r
+       tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
+               if (rng1 && rng2) {\r
+                       // Compare native IE ranges\r
+                       if (rng1.item || rng1.duplicate) {\r
+                               // Both are control ranges and the selected element matches\r
+                               if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))\r
+                                       return true;\r
+\r
+                               // Both are text ranges and the range matches\r
+                               if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))\r
+                                       return true;\r
+                       } else {\r
+                               // Compare w3c ranges\r
+                               return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;\r
+                       }\r
+               }\r
+\r
+               return false;\r
+       };\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Shorten class names\r
        var DOM = tinymce.DOM, is = tinymce.is;\r
@@ -6596,7 +6591,8 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                        tinymce.dom.Event.clear(this.id);\r
                }\r
        });\r
-})(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
+})(tinymce);\r
+tinymce.create('tinymce.ui.Container:tinymce.ui.Control', {\r
        Container : function(id, s) {\r
                this.parent(id, s);\r
 \r
@@ -6617,6 +6613,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
        }\r
 });\r
 \r
+\r
 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {\r
        Separator : function(id, s) {\r
                this.parent(id, s);\r
@@ -6627,6 +6624,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                return tinymce.DOM.createHTML('span', {'class' : this.classPrefix});\r
        }\r
 });\r
+\r
 (function(tinymce) {\r
        var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
 \r
@@ -6656,6 +6654,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk;\r
 \r
@@ -6753,7 +6752,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        return m;\r
                }\r
        });\r
-})(tinymce);(function(tinymce) {\r
+})(tinymce);\r
+(function(tinymce) {\r
        var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element;\r
 \r
        tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', {\r
@@ -7080,7 +7080,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        DOM.addClass(ro, 'mceLast');\r
                }\r
        });\r
-})(tinymce);(function(tinymce) {\r
+})(tinymce);\r
+(function(tinymce) {\r
        var DOM = tinymce.DOM;\r
 \r
        tinymce.create('tinymce.ui.Button:tinymce.ui.Control', {\r
@@ -7113,6 +7114,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
 \r
@@ -7324,7 +7326,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        var t = this, cp = t.classPrefix;\r
 \r
                        Event.add(t.id, 'click', t.showMenu, t);\r
-                       Event.add(t.id + '_text', 'focus', function(e) {\r
+                       Event.add(t.id + '_text', 'focus', function() {\r
                                if (!t._focused) {\r
                                        t.keyDownHandler = Event.add(t.id + '_text', 'keydown', function(e) {\r
                                                var idx = -1, v, kc = e.keyCode;\r
@@ -7382,7 +7384,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        Event.clear(this.id + '_open');\r
                }\r
        });\r
-})(tinymce);(function(tinymce) {\r
+})(tinymce);\r
+(function(tinymce) {\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;\r
 \r
        tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {\r
@@ -7455,7 +7458,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                },\r
 \r
                getLength : function() {\r
-                       return DOM.get(this.id).options.length - 1;\r
+                       return this.items.length;\r
                },\r
 \r
                renderHTML : function() {\r
@@ -7510,7 +7513,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        t.onPostRender.dispatch(t, DOM.get(t.id));\r
                }\r
        });\r
-})(tinymce);(function(tinymce) {\r
+})(tinymce);\r
+(function(tinymce) {\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
 \r
        tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', {\r
@@ -7599,6 +7603,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;\r
 \r
@@ -7664,6 +7669,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each;\r
 \r
@@ -7830,6 +7836,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                }\r
        });\r
 })(tinymce);\r
+\r
 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {\r
        renderHTML : function() {\r
                var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl;\r
@@ -7892,6 +7899,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>');\r
        }\r
 });\r
+\r
 (function(tinymce) {\r
        var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each;\r
 \r
@@ -8433,6 +8441,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        if (!t.getElement())\r
                                return;\r
 \r
+                       // Is a iPad/iPhone, then skip initialization. We need to sniff here since the\r
+                       // browser says it has contentEditable support but there is no visible caret\r
+                       // We will remove this check ones Apple implements full contentEditable support\r
+                       if (tinymce.isIDevice)\r
+                               return;\r
+\r
                        // Add hidden input for non input elements inside form elements\r
                        if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))\r
                                DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);\r
@@ -8798,18 +8812,19 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},\r
                                fontname : {inline : 'span', styles : {fontFamily : '%value'}},\r
                                fontsize : {inline : 'span', styles : {fontSize : '%value'}},\r
-                               blockquote : {block : 'blockquote', wrapper : 1},\r
+                               fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},\r
+                               blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},\r
 \r
                                removeformat : [\r
                                        {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},\r
                                        {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},\r
-                                       {selector : '*', attributes : ['style', 'class'], expand : false, deep : true}\r
+                                       {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}\r
                                ]\r
                        });\r
 \r
                        // Register default block formats\r
                        each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {\r
-                               t.formatter.register(name, {block : name});\r
+                               t.formatter.register(name, {block : name, remove : 'all'});\r
                        });\r
 \r
                        // Register user defined formats\r
@@ -9076,15 +9091,28 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
 \r
                focus : function(sf) {\r
-                       var oed, t = this, ce = t.settings.content_editable;\r
+                       var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();\r
 \r
                        if (!sf) {\r
-                               // Is not content editable or the selection is outside the area in IE\r
-                               // the IE statement is needed to avoid bluring if element selections inside layers since\r
-                               // the layer is like it's own document in IE\r
-                               if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc()))\r
+                               // Get selected control element\r
+                               ieRng = t.selection.getRng();\r
+                               if (ieRng.item) {\r
+                                       controlElm = ieRng.item(0);\r
+                               }\r
+\r
+                               // Is not content editable\r
+                               if (!ce)\r
                                        t.getWin().focus();\r
 \r
+                               // Restore selected control element\r
+                               // This is needed when for example an image is selected within a\r
+                               // layer a call to focus will then remove the control selection\r
+                               if (controlElm && controlElm.ownerDocument == doc) {\r
+                                       ieRng = doc.body.createControlRange();\r
+                                       ieRng.addElement(controlElm);\r
+                                       ieRng.select();\r
+                               }\r
+\r
                        }\r
 \r
                        if (tinymce.activeEditor != t) {\r
@@ -9160,7 +9188,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                },\r
 \r
                nodeChanged : function(o) {\r
-                       var t = this, s = t.selection, n = s.getNode() || t.getBody();\r
+                       var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody();\r
 \r
                        // Fix for bug #1896577 it seems that this can not be fired while the editor is loading\r
                        if (t.initialized) {\r
@@ -9850,7 +9878,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        // Add node change handlers\r
                        t.onMouseUp.add(t.nodeChanged);\r
-                       t.onClick.add(t.nodeChanged);\r
+                       //t.onClick.add(t.nodeChanged);\r
                        t.onKeyUp.add(function(ed, e) {\r
                                var c = e.keyCode;\r
 \r
@@ -9871,11 +9899,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
 \r
                                // Add default shortcuts for gecko\r
-                               if (isGecko) {\r
-                                       t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
-                                       t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
-                                       t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
-                               }\r
+                               t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');\r
+                               t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');\r
+                               t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');\r
 \r
                                // BlockFormat shortcuts keys\r
                                for (i=1; i<=6; i++)\r
@@ -9985,7 +10011,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                case 8:\r
                                                        // Fix IE control + backspace browser bug\r
                                                        if (t.selection.getRng().item) {\r
-                                                               t.selection.getRng().item(0).removeNode();\r
+                                                               ed.dom.remove(t.selection.getRng().item(0));\r
                                                                return Event.cancel(e);\r
                                                        }\r
                                        }\r
@@ -10026,6 +10052,48 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                });\r
 \r
                                t.onKeyDown.add(function(ed, e) {\r
+                                       var rng, parent, bookmark;\r
+\r
+                                       // IE has a really odd bug where the DOM might include an node that doesn't have\r
+                                       // a proper structure. If you try to access nodeValue it would throw an illegal value exception.\r
+                                       // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element\r
+                                       // after you delete contents from it. See: #3008923\r
+                                       if (isIE && e.keyCode == 46) {\r
+                                               rng = t.selection.getRng();\r
+\r
+                                               if (rng.parentElement) {\r
+                                                       parent = rng.parentElement();\r
+\r
+                                                       // Select next word when ctrl key is used in combo with delete\r
+                                                       if (e.ctrlKey) {\r
+                                                               rng.moveEnd('word', 1);\r
+                                                               rng.select();\r
+                                                       }\r
+\r
+                                                       // Delete contents\r
+                                                       t.selection.getSel().clear();\r
+\r
+                                                       // Check if we are within the same parent\r
+                                                       if (rng.parentElement() == parent) {\r
+                                                               bookmark = t.selection.getBookmark();\r
+\r
+                                                               try {\r
+                                                                       // Update the HTML and hopefully it will remove the artifacts\r
+                                                                       parent.innerHTML = parent.innerHTML;\r
+                                                               } catch (ex) {\r
+                                                                       // And since it's IE it can sometimes produce an unknown runtime error\r
+                                                               }\r
+\r
+                                                               // Restore the caret position\r
+                                                               t.selection.moveToBookmark(bookmark);\r
+                                                       }\r
+\r
+                                                       // Block the default delete behavior since it might be broken\r
+                                                       e.preventDefault();\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+\r
                                        // Is caracter positon keys\r
                                        if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {\r
                                                if (t.undoManager.typing)\r
@@ -10110,6 +10178,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Added for compression purposes\r
        var each = tinymce.each, undefined, TRUE = true, FALSE = false;\r
@@ -10221,7 +10290,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
 \r
                                // Present alert message about clipboard access not being available\r
-                               if (failed || !doc.queryCommandEnabled(command)) {\r
+                               if (failed || !doc.queryCommandSupported(command)) {\r
                                        if (tinymce.isGecko) {\r
                                                editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {\r
                                                        if (state)\r
@@ -10312,20 +10381,22 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        },\r
 \r
                        FormatBlock : function(command, ui, value) {\r
-                               return toggleFormat(value);\r
+                               return toggleFormat(value || 'p');\r
                        },\r
 \r
                        mceCleanup : function() {\r
-                               storeSelection();\r
+                               var bookmark = selection.getBookmark();\r
+\r
                                editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});\r
-                               restoreSelection();\r
+\r
+                               selection.moveToBookmark(bookmark);\r
                        },\r
 \r
                        mceRemoveNode : function(command, ui, value) {\r
                                var node = value || selection.getNode();\r
 \r
                                // Make sure that the body node isn't removed\r
-                               if (node != ed.getBody()) {\r
+                               if (node != editor.getBody()) {\r
                                        storeSelection();\r
                                        editor.dom.remove(node, TRUE);\r
                                        restoreSelection();\r
@@ -10398,6 +10469,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
                        },\r
 \r
+                       mceToggleFormat : function(command, ui, value) {\r
+                               editor.formatter.toggle(value);\r
+                       },\r
+\r
                        InsertHorizontalRule : function() {\r
                                selection.setContent('<hr />');\r
                        },\r
@@ -10426,8 +10501,17 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        if (value.href)\r
                                                dom.setAttribs(link, value);\r
                                        else\r
-                                               ed.dom.remove(link, TRUE);\r
+                                               editor.dom.remove(link, TRUE);\r
                                }\r
+                       },\r
+                       \r
+                       selectAll : function() {\r
+                               var root = dom.getRoot(), rng = dom.createRng();\r
+\r
+                               rng.setStart(root, 0);\r
+                               rng.setEnd(root, root.childNodes.length);\r
+\r
+                               editor.selection.setRng(rng);\r
                        }\r
                });\r
 \r
@@ -10494,122 +10578,120 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        });\r
                }\r
        };\r
-})(tinymce);(function(tinymce) {\r
-       tinymce.create('tinymce.UndoManager', {\r
-               index : 0,\r
-               data : null,\r
-               typing : 0,\r
+})(tinymce);\r
+(function(tinymce) {\r
+       var Dispatcher = tinymce.util.Dispatcher;\r
 \r
-               UndoManager : function(ed) {\r
-                       var t = this, Dispatcher = tinymce.util.Dispatcher;\r
+       tinymce.UndoManager = function(editor) {\r
+               var self, index = 0, data = [];\r
 \r
-                       t.editor = ed;\r
-                       t.data = [];\r
-                       t.onAdd = new Dispatcher(this);\r
-                       t.onUndo = new Dispatcher(this);\r
-                       t.onRedo = new Dispatcher(this);\r
-               },\r
+               function getContent() {\r
+                       return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));\r
+               };\r
 \r
-               add : function(l) {\r
-                       var t = this, i, ed = t.editor, b, s = ed.settings, la;\r
+               return self = {\r
+                       typing : 0,\r
 \r
-                       l = l || {};\r
-                       l.content = l.content || ed.getContent({format : 'raw', no_events : 1});\r
-                       l.content = l.content.replace(/^\s*|\s*$/g, '');\r
+                       onAdd : new Dispatcher(self),\r
+                       onUndo : new Dispatcher(self),\r
+                       onRedo : new Dispatcher(self),\r
 \r
-                       // Add undo level if needed\r
-                       la = t.data[t.index];\r
-                       if (la && la.content == l.content) {\r
-                               if (t.index > 0 || t.data.length == 1)\r
-                                       return null;\r
-                       }\r
+                       add : function(level) {\r
+                               var i, settings = editor.settings, lastLevel;\r
 \r
-                       // Time to compress\r
-                       if (s.custom_undo_redo_levels) {\r
-                               if (t.data.length > s.custom_undo_redo_levels) {\r
-                                       for (i = 0; i < t.data.length - 1; i++)\r
-                                               t.data[i] = t.data[i + 1];\r
+                               level = level || {};\r
+                               level.content = getContent();\r
 \r
-                                       t.data.length--;\r
-                                       t.index = t.data.length;\r
+                               // Add undo level if needed\r
+                               lastLevel = data[index];\r
+                               if (lastLevel && lastLevel.content == level.content) {\r
+                                       if (index > 0 || data.length == 1)\r
+                                               return null;\r
                                }\r
-                       }\r
 \r
-                       if (s.custom_undo_redo_restore_selection)\r
-                               l.bookmark = b = l.bookmark || ed.selection.getBookmark(2, true);\r
+                               // Time to compress\r
+                               if (settings.custom_undo_redo_levels) {\r
+                                       if (data.length > settings.custom_undo_redo_levels) {\r
+                                               for (i = 0; i < data.length - 1; i++)\r
+                                                       data[i] = data[i + 1];\r
 \r
-                       // Crop array if needed\r
-                       if (t.index < t.data.length - 1) {\r
-                               // Treat first level as initial\r
-                               if (t.index == 0)\r
-                                       t.data = [];\r
-                               else\r
-                                       t.data.length = t.index + 1;\r
-                       }\r
+                                               data.length--;\r
+                                               index = data.length;\r
+                                       }\r
+                               }\r
 \r
-                       t.data.push(l);\r
-                       t.index = t.data.length - 1;\r
+                               // Get a non intrusive normalized bookmark\r
+                               level.bookmark = editor.selection.getBookmark(2, true);\r
 \r
-                       t.onAdd.dispatch(t, l);\r
-                       ed.isNotDirty = 0;\r
+                               // Crop array if needed\r
+                               if (index < data.length - 1) {\r
+                                       // Treat first level as initial\r
+                                       if (index == 0)\r
+                                               data = [];\r
+                                       else\r
+                                               data.length = index + 1;\r
+                               }\r
 \r
-                       //console.log(t.index);\r
-                       //console.dir(t.data);\r
+                               data.push(level);\r
+                               index = data.length - 1;\r
 \r
-                       return l;\r
-               },\r
+                               self.onAdd.dispatch(self, level);\r
+                               editor.isNotDirty = 0;\r
 \r
-               undo : function() {\r
-                       var t = this, ed = t.editor, l = l, i;\r
+                               return level;\r
+                       },\r
 \r
-                       if (t.typing) {\r
-                               t.add();\r
-                               t.typing = 0;\r
-                       }\r
+                       undo : function() {\r
+                               var level, i;\r
 \r
-                       if (t.index > 0) {\r
-                               l = t.data[--t.index];\r
+                               if (self.typing) {\r
+                                       self.add();\r
+                                       self.typing = 0;\r
+                               }\r
 \r
-                               ed.setContent(l.content, {format : 'raw'});\r
-                               ed.selection.moveToBookmark(l.bookmark);\r
+                               if (index > 0) {\r
+                                       level = data[--index];\r
 \r
-                               t.onUndo.dispatch(t, l);\r
-                       }\r
+                                       editor.setContent(level.content, {format : 'raw'});\r
+                                       editor.selection.moveToBookmark(level.bookmark);\r
 \r
-                       return l;\r
-               },\r
+                                       self.onUndo.dispatch(self, level);\r
+                               }\r
 \r
-               redo : function() {\r
-                       var t = this, ed = t.editor, l = null;\r
+                               return level;\r
+                       },\r
 \r
-                       if (t.index < t.data.length - 1) {\r
-                               l = t.data[++t.index];\r
-                               ed.setContent(l.content, {format : 'raw'});\r
-                               ed.selection.moveToBookmark(l.bookmark);\r
+                       redo : function() {\r
+                               var level;\r
 \r
-                               t.onRedo.dispatch(t, l);\r
-                       }\r
+                               if (index < data.length - 1) {\r
+                                       level = data[++index];\r
 \r
-                       return l;\r
-               },\r
+                                       editor.setContent(level.content, {format : 'raw'});\r
+                                       editor.selection.moveToBookmark(level.bookmark);\r
 \r
-               clear : function() {\r
-                       var t = this;\r
+                                       self.onRedo.dispatch(self, level);\r
+                               }\r
 \r
-                       t.data = [];\r
-                       t.index = 0;\r
-                       t.typing = 0;\r
-               },\r
+                               return level;\r
+                       },\r
 \r
-               hasUndo : function() {\r
-                       return this.index > 0 || this.typing;\r
-               },\r
+                       clear : function() {\r
+                               data = [];\r
+                               index = self.typing = 0;\r
+                       },\r
 \r
-               hasRedo : function() {\r
-                       return this.index < this.data.length - 1;\r
-               }\r
-       });\r
+                       hasUndo : function() {\r
+                               return index > 0 || self.typing;\r
+                       },\r
+\r
+                       hasRedo : function() {\r
+                               return index < data.length - 1;\r
+                       }\r
+               };\r
+       };\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Shorten names\r
        var Event = tinymce.dom.Event,\r
@@ -10621,6 +10703,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                TRUE = true,\r
                FALSE = false;\r
 \r
+       function cloneFormats(node) {\r
+               var clone, temp, inner;\r
+\r
+               do {\r
+                       if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {\r
+                               if (clone) {\r
+                                       temp = node.cloneNode(false);\r
+                                       temp.appendChild(clone);\r
+                                       clone = temp;\r
+                               } else {\r
+                                       clone = inner = node.cloneNode(false);\r
+                               }\r
+\r
+                               clone.removeAttribute('id');\r
+                       }\r
+               } while (node = node.parentNode);\r
+\r
+               if (clone)\r
+                       return {wrapper : clone, inner : inner};\r
+       };\r
+\r
        // Checks if the selection/caret is at the end of the specified block element\r
        function isAtEnd(rng, par) {\r
                var rng2 = par.ownerDocument.createRange();\r
@@ -10729,11 +10832,54 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
                        }\r
 \r
-                       if (!isIE && s.force_p_newlines) {\r
-                               ed.onKeyPress.add(function(ed, e) {\r
-                                       if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
-                                               Event.cancel(e);\r
-                               });\r
+                       if (s.force_p_newlines) {\r
+                               if (!isIE) {\r
+                                       ed.onKeyPress.add(function(ed, e) {\r
+                                               if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))\r
+                                                       Event.cancel(e);\r
+                                       });\r
+                               } else {\r
+                                       // Ungly hack to for IE to preserve the formatting when you press\r
+                                       // enter at the end of a block element with formatted contents\r
+                                       // This logic overrides the browsers default logic with\r
+                                       // custom logic that enables us to control the output\r
+                                       tinymce.addUnload(function() {\r
+                                               t._previousFormats = 0; // Fix IE leak\r
+                                       });\r
+\r
+                                       ed.onKeyPress.add(function(ed, e) {\r
+                                               t._previousFormats = 0;\r
+\r
+                                               // Clone the current formats, this will later be applied to the new block contents\r
+                                               if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)\r
+                                                       t._previousFormats = cloneFormats(ed.selection.getStart());\r
+                                       });\r
+\r
+                                       ed.onKeyUp.add(function(ed, e) {\r
+                                               // Let IE break the element and the wrap the new caret location in the previous formats\r
+                                               if (e.keyCode == 13 && !e.shiftKey) {\r
+                                                       var parent = ed.selection.getStart(), fmt = t._previousFormats;\r
+\r
+                                                       // Parent is an empty block\r
+                                                       if (!parent.hasChildNodes()) {\r
+                                                               parent = dom.getParent(parent, dom.isBlock);\r
+\r
+                                                               if (parent) {\r
+                                                                       parent.innerHTML = '';\r
+       \r
+                                                                       if (t._previousFormats) {\r
+                                                                               parent.appendChild(fmt.wrapper);\r
+                                                                               fmt.inner.innerHTML = '\uFEFF';\r
+                                                                       } else\r
+                                                                               parent.innerHTML = '\uFEFF';\r
+\r
+                                                                       selection.select(parent, 1);\r
+                                                                       ed.getDoc().execCommand('Delete', false, null);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       });\r
+                               }\r
 \r
                                if (isGecko) {\r
                                        ed.onKeyDown.add(function(ed, e) {\r
@@ -10746,7 +10892,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973\r
                        if (tinymce.isWebKit) {\r
                                function insertBr(ed) {\r
-                                       var rng = selection.getRng(), br;\r
+                                       var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;\r
 \r
                                        // Insert BR element\r
                                        rng.insertNode(br = dom.create('br'));\r
@@ -10762,12 +10908,19 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                selection.collapse(TRUE);\r
                                        }\r
 \r
+                                       // Create a temporary DIV after the BR and get the position as it\r
+                                       // seems like getPos() returns 0 for text nodes and BR elements.\r
+                                       dom.insertAfter(div, br);\r
+                                       divYPos = dom.getPos(div).y;\r
+                                       dom.remove(div);\r
+\r
                                        // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117\r
-                                       ed.getWin().scrollTo(0, dom.getPos(selection.getRng().startContainer).y);\r
+                                       if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.\r
+                                               ed.getWin().scrollTo(0, divYPos);\r
                                };\r
 \r
                                ed.onKeyPress.add(function(ed, e) {\r
-                                       if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) {\r
+                                       if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {\r
                                                insertBr(ed);\r
                                                Event.cancel(e);\r
                                        }\r
@@ -10877,6 +11030,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                                                }\r
                                                                        }\r
                                                                } else {\r
+                                                                       // Force control range into text range\r
+                                                                       if (r.item) {\r
+                                                                               tr = d.body.createTextRange();\r
+                                                                               tr.moveToElementText(r.item(0));\r
+                                                                               r = tr;\r
+                                                                       }\r
+\r
                                                                        tr = d.body.createTextRange();\r
                                                                        tr.moveToElementText(b);\r
                                                                        tr.collapse(1);\r
@@ -11221,7 +11381,22 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                },\r
 \r
                backspaceDelete : function(e, bs) {\r
-                       var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn;\r
+                       var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;\r
+\r
+                       // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651\r
+                       if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {\r
+                               walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);\r
+\r
+                               // Walk the dom backwards until we find a text node\r
+                               for (n = sc.lastChild; n; n = walker.prev()) {\r
+                                       if (n.nodeType == 3) {\r
+                                               r.setStart(n, n.nodeValue.length);\r
+                                               r.collapse(true);\r
+                                               se.setRng(r);\r
+                                               return;\r
+                                       }\r
+                               }\r
+                       }\r
 \r
                        // The caret sometimes gets stuck in Gecko if you delete empty paragraphs\r
                        // This workaround removes the element by hand and moves the caret to the previous element\r
@@ -11252,40 +11427,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        }\r
                                }\r
                        }\r
-\r
-                       // Gecko generates BR elements here and there, we don't like those so lets remove them\r
-                       function handler(e) {\r
-                               var pr;\r
-\r
-                               e = e.target;\r
-\r
-                               // A new BR was created in a block element, remove it\r
-                               if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {\r
-                                       pr = e.previousSibling;\r
-\r
-                                       Event.remove(b, 'DOMNodeInserted', handler);\r
-\r
-                                       // Is there whitespace at the end of the node before then we might need the pesky BR\r
-                                       // to place the caret at a correct location see bug: #2013943\r
-                                       if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))\r
-                                               return;\r
-\r
-                                       // Only remove BR elements that got inserted in the middle of the text\r
-                                       if (e.previousSibling || e.nextSibling)\r
-                                               ed.dom.remove(e);\r
-                               }\r
-                       };\r
-\r
-                       // Listen for new nodes\r
-                       Event._add(b, 'DOMNodeInserted', handler);\r
-\r
-                       // Remove listener\r
-                       window.setTimeout(function() {\r
-                               Event._remove(b, 'DOMNodeInserted', handler);\r
-                       }, 1);\r
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Shorten names\r
        var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend;\r
@@ -11647,6 +11792,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera;\r
 \r
@@ -11761,7 +11907,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        return tinymce.DOM.decode(s).replace(/\\n/g, '\n');\r
                }\r
        });\r
-}(tinymce));(function(tinymce) {\r
+}(tinymce));\r
+(function(tinymce) {\r
        function CommandManager() {\r
                var execCommands = {}, queryStateCommands = {}, queryValueCommands = {};\r
 \r
@@ -11807,7 +11954,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
        };\r
 \r
        tinymce.GlobalCommands = new CommandManager();\r
-})(tinymce);(function(tinymce) {\r
+})(tinymce);\r
+(function(tinymce) {\r
        tinymce.Formatter = function(ed) {\r
                var formats = {},\r
                        each = tinymce.each,\r
@@ -11824,22 +11972,20 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        FALSE = false,\r
                        TRUE = true,\r
                        undefined,\r
-                       caretHandler,\r
-                       pendingFormats;\r
+                       pendingFormats = {apply : [], remove : []};\r
+\r
+               function isArray(obj) {\r
+                       return obj instanceof Array;\r
+               };\r
 \r
                function getParents(node, selector) {\r
                        return dom.getParents(node, selector, dom.getRoot());\r
                };\r
 \r
-               function resetPending() {\r
-                       // Needs reset\r
-                       if (!pendingFormats || pendingFormats.apply.length || pendingFormats.remove.length)\r
-                               pendingFormats = {apply : [], remove : []};\r
+               function isCaretNode(node) {\r
+                       return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
                };\r
 \r
-               ed.onMouseUp.add(resetPending);\r
-               resetPending();\r
-\r
                // Public functions\r
 \r
                function get(name) {\r
@@ -11864,12 +12010,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                                // Default to true\r
                                                if (format.split === undefined)\r
-                                                       format.split = !format.selector;\r
+                                                       format.split = !format.selector || format.inline;\r
 \r
                                                // Default to true\r
-                                               if (format.remove === undefined && format.selector)\r
+                                               if (format.remove === undefined && format.selector && !format.inline)\r
                                                        format.remove = 'none';\r
 \r
+                                               // Mark format as a mixed format inline + block level\r
+                                               if (format.selector && format.inline) {\r
+                                                       format.mixed = true;\r
+                                                       format.block_expand = true;\r
+                                               }\r
+\r
                                                // Split classes if needed\r
                                                if (typeof(format.classes) === 'string')\r
                                                        format.classes = format.classes.split(/\s+/);\r
@@ -11890,11 +12042,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                // Move startContainer/startOffset in to a suitable node\r
                                if (container.nodeType == 1 || container.nodeValue === "") {\r
-                                       walker = new TreeWalker(container.childNodes[offset]);\r
-                                       for (node = walker.current(); node; node = walker.next()) {\r
-                                               if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) {\r
-                                                       rng.setStart(node, 0);\r
-                                                       break;\r
+                                       container = container.nodeType == 1 ? container.childNodes[offset] : container;\r
+\r
+                                       // Might fail if the offset is behind the last element in it's container\r
+                                       if (container) {\r
+                                               walker = new TreeWalker(container, container.parentNode);\r
+                                               for (node = walker.current(); node; node = walker.next()) {\r
+                                                       if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
+                                                               rng.setStart(node, 0);\r
+                                                               break;\r
+                                                       }\r
                                                }\r
                                        }\r
                                }\r
@@ -11935,7 +12092,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        var currentWrapElm;\r
 \r
                                        function process(node) {\r
-                                               var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase();\r
+                                               var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;\r
 \r
                                                // Stop wrapping on br elements\r
                                                if (isEq(nodeName, 'br')) {\r
@@ -11967,11 +12124,17 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                if (format.selector) {\r
                                                        // Look for matching formats\r
                                                        each(formatList, function(format) {\r
-                                                               if (dom.is(node, format.selector))\r
+                                                               if (dom.is(node, format.selector) && !isCaretNode(node)) {\r
                                                                        setElementFormat(node, format);\r
+                                                                       found = true;\r
+                                                               }\r
                                                        });\r
 \r
-                                                       return;\r
+                                                       // Continue processing if a selector match wasn't found and a inline element is defined\r
+                                                       if (!format.inline || found) {\r
+                                                               currentWrapElm = 0;\r
+                                                               return;\r
+                                                       }\r
                                                }\r
 \r
                                                // Is it valid to wrap this item\r
@@ -12019,7 +12182,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                var child, clone;\r
 \r
                                                each(node.childNodes, function(node) {\r
-                                                       if (node.nodeType == 1 && !isBookmarkNode(node)) {\r
+                                                       if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) {\r
                                                                child = node;\r
                                                                return FALSE; // break loop\r
                                                        }\r
@@ -12032,9 +12195,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                                        dom.replace(clone, node, TRUE);\r
                                                        dom.remove(child, 1);\r
-\r
-                                                       return TRUE;\r
                                                }\r
+\r
+                                               return clone || node;\r
                                        };\r
 \r
                                        childCount = getChildCount(node);\r
@@ -12047,10 +12210,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                        if (format.inline || format.wrapper) {\r
                                                // Merges the current node with it's children of similar type to reduce the number of elements\r
-                                               if (!format.exact && childCount === 1) {\r
-                                                       if (mergeStyles(node))\r
-                                                               return;\r
-                                               }\r
+                                               if (!format.exact && childCount === 1)\r
+                                                       node = mergeStyles(node);\r
 \r
                                                // Remove/merge children\r
                                                each(formatList, function(format) {\r
@@ -12062,14 +12223,23 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                        });\r
                                                });\r
 \r
+                                               // Remove child if direct parent is of same type\r
+                                               if (matchNode(node.parentNode, name, vars)) {\r
+                                                       dom.remove(node, 1);\r
+                                                       node = 0;\r
+                                                       return TRUE;\r
+                                               }\r
+\r
                                                // Look for parent with similar style format\r
-                                               dom.getParent(node.parentNode, function(parent) {\r
-                                                       if (matchNode(parent, name, vars)) {\r
-                                                               dom.remove(node, 1);\r
-                                                               node = 0;\r
-                                                               return TRUE;\r
-                                                       }\r
-                                               });\r
+                                               if (format.merge_with_parents) {\r
+                                                       dom.getParent(node.parentNode, function(parent) {\r
+                                                               if (matchNode(parent, name, vars)) {\r
+                                                                       dom.remove(node, 1);\r
+                                                                       node = 0;\r
+                                                                       return TRUE;\r
+                                                               }\r
+                                                       });\r
+                                               }\r
 \r
                                                // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>\r
                                                if (node) {\r
@@ -12087,7 +12257,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        rng.setStartBefore(node);\r
                                        rng.setEndAfter(node);\r
 \r
-                                       applyRngStyle(rng);\r
+                                       applyRngStyle(expandRng(rng, formatList));\r
                                } else {\r
                                        if (!selection.isCollapsed() || !format.inline) {\r
                                                // Apply formatting to selection\r
@@ -12106,6 +12276,45 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                function remove(name, vars, node) {\r
                        var formatList = get(name), format = formatList[0], bookmark, i, rng;\r
 \r
+                       function moveStart(rng) {\r
+                               var container = rng.startContainer,\r
+                                       offset = rng.startOffset,\r
+                                       walker, node, nodes, tmpNode;\r
+\r
+                               // Convert text node into index if possible\r
+                               if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {\r
+                                       container = container.parentNode;\r
+                                       offset = nodeIndex(container) + 1;\r
+                               }\r
+\r
+                               // Move startContainer/startOffset in to a suitable node\r
+                               if (container.nodeType == 1) {\r
+                                       nodes = container.childNodes;\r
+                                       container = nodes[Math.min(offset, nodes.length - 1)];\r
+                                       walker = new TreeWalker(container);\r
+\r
+                                       // If offset is at end of the parent node walk to the next one\r
+                                       if (offset > nodes.length - 1)\r
+                                               walker.next();\r
+\r
+                                       for (node = walker.current(); node; node = walker.next()) {\r
+                                               if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {\r
+                                                       // IE has a "neat" feature where it moves the start node into the closest element\r
+                                                       // we can avoid this by inserting an element before it and then remove it after we set the selection\r
+                                                       tmpNode = dom.create('a', null, INVISIBLE_CHAR);\r
+                                                       node.parentNode.insertBefore(tmpNode, node);\r
+\r
+                                                       // Set selection and remove tmpNode\r
+                                                       rng.setStart(node, 0);\r
+                                                       selection.setRng(rng);\r
+                                                       dom.remove(tmpNode);\r
+\r
+                                                       return;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       };\r
+\r
                        // Merges the styles for each node\r
                        function process(node) {\r
                                var children, i, l;\r
@@ -12131,10 +12340,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                // Find format root\r
                                each(getParents(container.parentNode).reverse(), function(parent) {\r
+                                       var format;\r
+\r
                                        // Find format root element\r
                                        if (!formatRoot && parent.id != '_start' && parent.id != '_end') {\r
-                                               // If the matched format has a remove none flag we shouldn't split it\r
-                                               if (!isBlock(parent) && matchNode(parent, name, vars))\r
+                                               // Is the node matching the format we are looking for\r
+                                               format = matchNode(parent, name, vars);\r
+                                               if (format && format.split !== false)\r
                                                        formatRoot = parent;\r
                                        }\r
                                });\r
@@ -12171,7 +12383,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                }\r
                                        }\r
 \r
-                                       if (split)\r
+                                       // Never split block elements if the format is mixed\r
+                                       if (split && (!format.mixed || !isBlock(format_root)))\r
                                                container = dom.split(format_root, container);\r
 \r
                                        // Wrap container in cloned formats\r
@@ -12192,7 +12405,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                var node = dom.get(start ? '_start' : '_end'),\r
                                        out = node[start ? 'firstChild' : 'lastChild'];\r
 \r
-                               dom.remove(node, 1);\r
+                               // If the end is placed within the start the result will be removed\r
+                               // So this checks if the out node is a bookmark node if it is it\r
+                               // checks for another more suitable node\r
+                               if (isBookmarkNode(out))\r
+                                       out = out[start ? 'firstChild' : 'lastChild'];\r
+\r
+                               dom.remove(node, true);\r
 \r
                                return out;\r
                        };\r
@@ -12249,6 +12468,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                bookmark = selection.getBookmark();\r
                                removeRngStyle(selection.getRng(TRUE));\r
                                selection.moveToBookmark(bookmark);\r
+\r
+                               // Check if start element still has formatting then we are at: "<b>text|</b>text" and need to move the start into the next text node\r
+                               if (match(name, vars, selection.getStart())) {\r
+                                       moveStart(selection.getRng(true));\r
+                               }\r
+\r
                                ed.nodeChanged();\r
                        } else\r
                                performCaretAction('remove', name, vars);\r
@@ -12261,7 +12486,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                apply(name, vars, node);\r
                };\r
 \r
-               function matchNode(node, name, vars) {\r
+               function matchNode(node, name, vars, similar) {\r
                        var formatList = get(name), format, i, classes;\r
 \r
                        function matchItems(node, format, item_name) {\r
@@ -12278,7 +12503,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                                else\r
                                                                        value = getStyle(node, key);\r
 \r
-                                                               if (!isEq(value, replaceVars(items[key], vars)))\r
+                                                               if (similar && !value && !format.exact)\r
+                                                                       return;\r
+\r
+                                                               if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))\r
                                                                        return;\r
                                                        }\r
                                                }\r
@@ -12286,12 +12514,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                // Only one match needed for indexed arrays\r
                                                for (i = 0; i < items.length; i++) {\r
                                                        if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i]))\r
-                                                               return TRUE;\r
+                                                               return format;\r
                                                }\r
                                        }\r
                                }\r
 \r
-                               return TRUE;\r
+                               return format;\r
                        };\r
 \r
                        if (formatList && node) {\r
@@ -12309,7 +12537,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                        }\r
                                                }\r
 \r
-                                               return TRUE;\r
+                                               return format;\r
                                        }\r
                                }\r
                        }\r
@@ -12321,7 +12549,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        function matchParents(node) {\r
                                // Find first node with similar format settings\r
                                node = dom.getParent(node, function(node) {\r
-                                       return !!matchNode(node, name, vars);\r
+                                       return !!matchNode(node, name, vars, true);\r
                                });\r
 \r
                                // Do an exact check on the similar format element\r
@@ -12362,6 +12590,54 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        return FALSE;\r
                };\r
 \r
+               function matchAll(names, vars) {\r
+                       var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name;\r
+\r
+                       // If the selection is collapsed then check pending formats\r
+                       if (selection.isCollapsed()) {\r
+                               for (ni = 0; ni < names.length; ni++) {\r
+                                       // If the name is to be removed, then stop it from being added\r
+                                       for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
+                                               name = names[ni];\r
+\r
+                                               if (pendingFormats.remove[i].name == name) {\r
+                                                       checkedMap[name] = true;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               // If the format is to be applied\r
+                               for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
+                                       for (ni = 0; ni < names.length; ni++) {\r
+                                               name = names[ni];\r
+\r
+                                               if (!checkedMap[name] && pendingFormats.apply[i].name == name) {\r
+                                                       checkedMap[name] = true;\r
+                                                       matchedFormatNames.push(name);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // Check start of selection for formats\r
+                       startElement = selection.getStart();\r
+                       dom.getParent(startElement, function(node) {\r
+                               var i, name;\r
+\r
+                               for (i = 0; i < names.length; i++) {\r
+                                       name = names[i];\r
+\r
+                                       if (!checkedMap[name] && matchNode(node, name, vars)) {\r
+                                               checkedMap[name] = true;\r
+                                               matchedFormatNames.push(name);\r
+                                       }\r
+                               }\r
+                       });\r
+\r
+                       return matchedFormatNames;\r
+               };\r
+\r
                function canApply(name) {\r
                        var formatList = get(name), startNode, parents, i, x, selector;\r
 \r
@@ -12394,6 +12670,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        remove : remove,\r
                        toggle : toggle,\r
                        match : match,\r
+                       matchAll : matchAll,\r
                        matchNode : matchNode,\r
                        canApply : canApply\r
                });\r
@@ -12418,8 +12695,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        str1 = str1 || '';\r
                        str2 = str2 || '';\r
 \r
-                       str1 = str1.nodeName || str1;\r
-                       str2 = str2.nodeName || str2;\r
+                       str1 = '' + (str1.nodeName || str1);\r
+                       str2 = '' + (str2.nodeName || str2);\r
 \r
                        return str1.toLowerCase() == str2.toLowerCase();\r
                };\r
@@ -12451,7 +12728,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function isWhiteSpaceNode(node) {\r
-                       return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue);\r
+                       return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
                };\r
 \r
                function wrap(node, name, attrs) {\r
@@ -12538,7 +12815,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        }\r
 \r
                        // Expand start/end container to matching selector\r
-                       if (format[0].selector && format[0].expand !== FALSE) {\r
+                       if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {\r
                                function findSelectorEndPoint(container, sibling_name) {\r
                                        var parents, i, y;\r
 \r
@@ -12765,6 +13042,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
                        }\r
 \r
+                       // Never remove nodes that isn't the specified inline element if a selector is specified too\r
+                       if (format.selector && format.inline && !isEq(format.inline, node))\r
+                               return;\r
+\r
                        dom.remove(node, 1);\r
                };\r
 \r
@@ -12797,8 +13078,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        each(dom.getAttribs(node), function(attr) {\r
                                                var name = attr.nodeName.toLowerCase();\r
 \r
-                                               // Don't compare internal attributes or style/class\r
-                                               if (name.indexOf('_') !== 0 && name !== 'class' && name !== 'style')\r
+                                               // Don't compare internal attributes or style\r
+                                               if (name.indexOf('_') !== 0 && name !== 'style')\r
                                                        attribs[name] = dom.getAttrib(node, name);\r
                                        });\r
 \r
@@ -12890,7 +13171,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function isTextBlock(name) {\r
-                       return /^(h[1-6]|p|div|pre|address)$/.test(name);\r
+                       return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);\r
                };\r
 \r
                function getContainer(rng, start) {\r
@@ -12912,34 +13193,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function performCaretAction(type, name, vars) {\r
-                       var i, rng, selectedNode = selection.getNode().parentNode,\r
-                               doc = ed.getDoc(), marker = 'mceinline',\r
-                               events = ['onKeyDown', 'onKeyUp', 'onKeyPress'],\r
-                               currentPendingFormats = pendingFormats[type],\r
+                       var i, currentPendingFormats = pendingFormats[type],\r
                                otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
 \r
-                       // Check if it already exists\r
-                       for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
-                               if (currentPendingFormats[i].name == name)\r
-                                       return;\r
-                       }\r
-\r
-                       currentPendingFormats.push({name : name, vars : vars});\r
-\r
-                       // Check if it's in the oter type\r
-                       for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
-                               if (otherPendingFormats[i].name == name)\r
-                                       otherPendingFormats.splice(i, 1);\r
-                       }\r
-\r
-                       function unbind() {\r
-                               if (caretHandler) {\r
-                                       each(events, function(event) {\r
-                                               ed[event].remove(caretHandler);\r
-                                       });\r
+                       function hasPending() {\r
+                               return pendingFormats.apply.length || pendingFormats.remove.length;\r
+                       };\r
 \r
-                                       caretHandler = 0;\r
-                               }\r
+                       function resetPending() {\r
+                               pendingFormats.apply = [];\r
+                               pendingFormats.remove = [];\r
                        };\r
 \r
                        function perform(caret_node) {\r
@@ -12957,62 +13220,77 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                resetPending();\r
                        };\r
 \r
-                       function isMarker(node) {\r
-                               return node.face == marker || node.style.fontFamily == marker;\r
-                       };\r
-\r
-                       unbind();\r
-\r
-                       doc.execCommand('FontName', false, marker);\r
+                       // Check if it already exists then ignore it\r
+                       for (i = currentPendingFormats.length - 1; i >= 0; i--) {\r
+                               if (currentPendingFormats[i].name == name)\r
+                                       return;\r
+                       }\r
 \r
-                       // IE will convert the current word\r
-                       each(dom.select('font,span', selectedNode), function(node) {\r
-                               var bookmark;\r
+                       currentPendingFormats.push({name : name, vars : vars});\r
 \r
-                               if (isMarker(node)) {\r
-                                       bookmark = selection.getBookmark();\r
-                                       perform(node);\r
-                                       selection.moveToBookmark(bookmark);\r
-                                       ed.nodeChanged();\r
-                                       selectedNode = 0;\r
-                               }\r
-                       });\r
+                       // Check if it's in the other type, then remove it\r
+                       for (i = otherPendingFormats.length - 1; i >= 0; i--) {\r
+                               if (otherPendingFormats[i].name == name)\r
+                                       otherPendingFormats.splice(i, 1);\r
+                       }\r
 \r
-                       if (selectedNode) {\r
-                               caretHandler = function(ed, e) {\r
-                                       each(dom.select('font,span', selectedNode), function(node) {\r
-                                               var bookmark, textNode;\r
+                       // Pending apply or remove formats\r
+                       if (hasPending()) {\r
+                               ed.getDoc().execCommand('FontName', false, 'mceinline');\r
+                               pendingFormats.lastRng = selection.getRng();\r
 \r
-                                               // Look for marker\r
-                                               if (node.face == marker || node.style.fontFamily == marker) {\r
-                                                       textNode = node.firstChild;\r
+                               // IE will convert the current word\r
+                               each(dom.select('font,span'), function(node) {\r
+                                       var bookmark;\r
 \r
-                                                       perform(node);\r
+                                       if (isCaretNode(node)) {\r
+                                               bookmark = selection.getBookmark();\r
+                                               perform(node);\r
+                                               selection.moveToBookmark(bookmark);\r
+                                               ed.nodeChanged();\r
+                                       }\r
+                               });\r
 \r
-                                                       rng = dom.createRng();\r
-                                                       rng.setStart(textNode, textNode.nodeValue.length);\r
-                                                       rng.setEnd(textNode, textNode.nodeValue.length);\r
-                                                       selection.setRng(rng);\r
-                                                       ed.nodeChanged();\r
+                               // Only register listeners once if we need to\r
+                               if (!pendingFormats.isListening && hasPending()) {\r
+                                       pendingFormats.isListening = true;\r
+\r
+                                       each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
+                                               ed[event].addToTop(function(ed, e) {\r
+                                                       // Do we have pending formats and is the selection moved has moved\r
+                                                       if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {\r
+                                                               each(dom.select('font,span'), function(node) {\r
+                                                                       var textNode, rng;\r
+\r
+                                                                       // Look for marker\r
+                                                                       if (isCaretNode(node)) {\r
+                                                                               textNode = node.firstChild;\r
+\r
+                                                                               if (textNode) {\r
+                                                                                       perform(node);\r
+\r
+                                                                                       rng = dom.createRng();\r
+                                                                                       rng.setStart(textNode, textNode.nodeValue.length);\r
+                                                                                       rng.setEnd(textNode, textNode.nodeValue.length);\r
+                                                                                       selection.setRng(rng);\r
+                                                                                       ed.nodeChanged();\r
+                                                                               } else\r
+                                                                                       dom.remove(node);\r
+                                                                       }\r
+                                                               });\r
 \r
-                                                       unbind();\r
-                                               }\r
+                                                               // Always unbind and clear pending styles on keyup\r
+                                                               if (e.type == 'keyup' || e.type == 'mouseup')\r
+                                                                       resetPending();\r
+                                                       }\r
+                                               });\r
                                        });\r
-\r
-                                       // Always unbind and clear pending styles on keyup\r
-                                       if (e.type == 'keyup') {\r
-                                               unbind();\r
-                                               resetPending();\r
-                                       }\r
-                               };\r
-\r
-                               each(events, function(event) {\r
-                                       ed[event].addToTop(caretHandler);\r
-                               });\r
+                               }\r
                        }\r
-               }\r
+               };\r
        };\r
 })(tinymce);\r
+\r
 tinymce.onAddEditor.add(function(tinymce, ed) {\r
        var filters, fontSizes, dom, settings = ed.settings;\r
 \r
@@ -13065,3 +13343,4 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
                });\r
        }\r
 });\r
+\r