Updated tiny-mce to most recent 3.4 version
[citadel.git] / webcit / tiny_mce / tiny_mce_src.js
index 8c665b09535c46d2bdac3012c7b881f0948b6e2c..64feb6663762f61f030fb01d3f7abf8f816900ac 100644 (file)
@@ -5,9 +5,9 @@
        var tinymce = {\r
                majorVersion : '3',\r
 \r
-               minorVersion : '4.5',\r
+               minorVersion : '4.9',\r
 \r
-               releaseDate : '2011-09-06',\r
+               releaseDate : '2012-02-23',\r
 \r
                _init : function() {\r
                        var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;\r
@@ -535,7 +535,7 @@ tinymce.create('tinymce.util.Dispatcher', {
                // And this is also more efficient\r
                for (i = 0; i<li.length; i++) {\r
                        c = li[i];\r
-                       s = c.cb.apply(c.scope, a);\r
+                       s = c.cb.apply(c.scope, a.length > 0 ? a : [c.scope]);\r
 \r
                        if (s === false)\r
                                break;\r
@@ -578,7 +578,7 @@ tinymce.create('tinymce.util.Dispatcher', {
 \r
                        // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)\r
                        u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something\r
-                       u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
+                       u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);\r
                        each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {\r
                                var s = u[i];\r
 \r
@@ -901,8 +901,11 @@ tinymce.create('tinymce.util.Dispatcher', {
 \r
                                v = '{';\r
 \r
-                               for (i in o)\r
-                                       v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';\r
+                               for (i in o) {\r
+                                       if (o.hasOwnProperty(i)) {\r
+                                               v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';\r
+                                       }\r
+                               }\r
 \r
                                return v + '}';\r
                }\r
@@ -923,6 +926,7 @@ tinymce.create('tinymce.util.Dispatcher', {
 \r
                };\r
 })();\r
+\r
 tinymce.create('static tinymce.util.XHR', {\r
        send : function(o) {\r
                var x, t, w = window, c = 0;\r
@@ -1038,11 +1042,17 @@ tinymce.create('static tinymce.util.XHR', {
 }());\r
 (function(tinymce){\r
        tinymce.VK = {\r
-               DELETE:46,\r
-               BACKSPACE:8\r
-               \r
+               DELETE: 46,\r
+               BACKSPACE: 8,\r
+               ENTER: 13,\r
+               TAB: 9,\r
+        SPACEBAR: 32,\r
+               UP: 38,\r
+               DOWN: 40,\r
+               modifierPressed: function (e) {\r
+                       return e.shiftKey || e.ctrlKey || e.altKey;\r
+               }\r
        }\r
-\r
 })(tinymce);\r
 \r
 (function(tinymce) {\r
@@ -1055,7 +1065,7 @@ tinymce.create('static tinymce.util.XHR', {
                        var rng, blockElm, node, clonedSpan, isDelete;\r
 \r
                        isDelete = e.keyCode == DELETE;\r
-                       if (isDelete || e.keyCode == BACKSPACE) {\r
+                       if ((isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {\r
                                e.preventDefault();\r
                                rng = selection.getRng();\r
 \r
@@ -1070,22 +1080,22 @@ tinymce.create('static tinymce.util.XHR', {
                                if (blockElm) {\r
                                        node = blockElm.firstChild;\r
 \r
+                                       // Ignore empty text nodes\r
+                                       while (node && node.nodeType == 3 && node.nodeValue.length == 0)\r
+                                               node = node.nextSibling;\r
+\r
                                        if (node && node.nodeName === 'SPAN') {\r
                                                clonedSpan = node.cloneNode(false);\r
                                        }\r
                                }\r
 \r
-                               // Do the backspace/delete actiopn\r
+                               // Do the backspace/delete action\r
                                ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);\r
 \r
                                // Find all odd apple-style-spans\r
                                blockElm = dom.getParent(rng.startContainer, dom.isBlock);\r
                                tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {\r
-                                       var rng = dom.createRng();\r
-\r
-                                       // Set range selection before the span we are about to remove\r
-                                       rng.setStartBefore(span);\r
-                                       rng.setEndBefore(span);\r
+                                       var bm = selection.getBookmark();\r
 \r
                                        if (clonedSpan) {\r
                                                dom.replace(clonedSpan.cloneNode(false), span, true);\r
@@ -1094,45 +1104,184 @@ tinymce.create('static tinymce.util.XHR', {
                                        }\r
 \r
                                        // Restore the selection\r
-                                       selection.setRng(rng);\r
+                                       selection.moveToBookmark(bm);\r
                                });\r
                        }\r
                });\r
        };\r
 \r
        function emptyEditorWhenDeleting(ed) {\r
-               ed.onKeyUp.add(function(ed, e) {\r
-                       var keyCode = e.keyCode;\r
 \r
+               function serializeRng(rng) {\r
+                       var body = ed.dom.create("body");\r
+                       var contents = rng.cloneContents();\r
+                       body.appendChild(contents);\r
+                       return ed.selection.serializer.serialize(body, {format: 'html'});\r
+               }\r
+\r
+               function allContentsSelected(rng) {\r
+                       var selection = serializeRng(rng);\r
+\r
+                       var allRng = ed.dom.createRng();\r
+                       allRng.selectNode(ed.getBody());\r
+\r
+                       var allSelection = serializeRng(allRng);\r
+                       return selection === allSelection;\r
+               }\r
+\r
+               ed.onKeyDown.addToTop(function(ed, e) {\r
+                       var keyCode = e.keyCode;\r
                        if (keyCode == DELETE || keyCode == BACKSPACE) {\r
-                               if (ed.dom.isEmpty(ed.getBody())) {\r
+                               var rng = ed.selection.getRng(true);\r
+                               if (!rng.collapsed && allContentsSelected(rng)) {\r
                                        ed.setContent('', {format : 'raw'});\r
                                        ed.nodeChanged();\r
-                                       return;\r
+                                       e.preventDefault();\r
                                }\r
                        }\r
                });\r
+\r
+       };\r
+\r
+       function inputMethodFocus(ed) {\r
+               ed.dom.bind(ed.getDoc(), 'focusin', function() {\r
+                       ed.selection.setRng(ed.selection.getRng());\r
+               });\r
        };\r
+\r
+       function removeHrOnBackspace(ed) {\r
+               ed.onKeyDown.add(function(ed, e) {\r
+                       if (e.keyCode === BACKSPACE) {\r
+                               if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {\r
+                                       var node = ed.selection.getNode();\r
+                                       var previousSibling = node.previousSibling;\r
+                                       if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {\r
+                                               ed.dom.remove(previousSibling);\r
+                                               tinymce.dom.Event.cancel(e);\r
+                                       }\r
+                               }\r
+                       }\r
+               })\r
+       }\r
+\r
+       function focusBody(ed) {\r
+               // Fix for a focus bug in FF 3.x where the body element\r
+               // wouldn't get proper focus if the user clicked on the HTML element\r
+               if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4\r
+                       ed.onMouseDown.add(function(ed, e) {\r
+                               if (e.target.nodeName === "HTML") {\r
+                                       var body = ed.getBody();\r
+\r
+                                       // Blur the body it's focused but not correctly focused\r
+                                       body.blur();\r
+\r
+                                       // Refocus the body after a little while\r
+                                       setTimeout(function() {\r
+                                               body.focus();\r
+                                       }, 0);\r
+                               }\r
+                       });\r
+               }\r
+       };\r
+\r
+       function selectControlElements(ed) {\r
+               ed.onClick.add(function(ed, e) {\r
+                       e = e.target;\r
+\r
+                       // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
+                       // WebKit can't even do simple things like selecting an image\r
+                       // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
+                       if (/^(IMG|HR)$/.test(e.nodeName))\r
+                               ed.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
+\r
+                       if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor'))\r
+                               ed.selection.select(e);\r
+\r
+                       ed.nodeChanged();\r
+               });\r
+       };\r
+\r
+       function removeStylesOnPTagsInheritedFromHeadingTag(ed) {\r
+               ed.onKeyDown.add(function(ed, event) {\r
+                       function checkInHeadingTag(ed) {\r
+                               var currentNode = ed.selection.getNode();\r
+                               var headingTags = 'h1,h2,h3,h4,h5,h6';\r
+                               return ed.dom.is(currentNode, headingTags) || ed.dom.getParent(currentNode, headingTags) !== null;\r
+                       }\r
+\r
+                       if (event.keyCode === VK.ENTER && !VK.modifierPressed(event) && checkInHeadingTag(ed)) {\r
+                               setTimeout(function() {\r
+                                       var currentNode = ed.selection.getNode();\r
+                                       if (ed.dom.is(currentNode, 'p')) {\r
+                                               ed.dom.setAttrib(currentNode, 'style', null);\r
+                                               // While tiny's content is correct after this method call, the content shown is not representative of it and needs to be 'repainted'\r
+                                               ed.execCommand('mceCleanup');\r
+                                       }\r
+                               }, 0);\r
+                       }\r
+               });\r
+       }\r
+       function selectionChangeNodeChanged(ed) {\r
+               var lastRng, selectionTimer;\r
+\r
+               ed.dom.bind(ed.getDoc(), 'selectionchange', function() {\r
+                       if (selectionTimer) {\r
+                               clearTimeout(selectionTimer);\r
+                               selectionTimer = 0;\r
+                       }\r
+\r
+                       selectionTimer = window.setTimeout(function() {\r
+                               var rng = ed.selection.getRng();\r
+\r
+                               // Compare the ranges to see if it was a real change or not\r
+                               if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {\r
+                                       ed.nodeChanged();\r
+                                       lastRng = rng;\r
+                               }\r
+                       }, 50);\r
+               });\r
+       }\r
+\r
+       function ensureBodyHasRoleApplication(ed) {\r
+               document.body.setAttribute("role", "application");\r
+       }\r
        \r
        tinymce.create('tinymce.util.Quirks', {\r
                Quirks: function(ed) {\r
-                       // Load WebKit specific fixed\r
+                       // WebKit\r
                        if (tinymce.isWebKit) {\r
                                cleanupStylesWhenDeleting(ed);\r
                                emptyEditorWhenDeleting(ed);\r
+                               inputMethodFocus(ed);\r
+                               selectControlElements(ed);\r
+\r
+                               // iOS\r
+                               if (tinymce.isIDevice) {\r
+                                       selectionChangeNodeChanged(ed);\r
+                               }\r
                        }\r
 \r
-                       // Load IE specific fixes\r
+                       // IE\r
                        if (tinymce.isIE) {\r
+                               removeHrOnBackspace(ed);\r
                                emptyEditorWhenDeleting(ed);\r
+                               ensureBodyHasRoleApplication(ed);\r
+                               removeStylesOnPTagsInheritedFromHeadingTag(ed)\r
+                       }\r
+\r
+                       // Gecko\r
+                       if (tinymce.isGecko) {\r
+                               removeHrOnBackspace(ed);\r
+                               focusBody(ed);\r
                        }\r
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        var namedEntities, baseEntities, reverseEntities,\r
-               attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
-               textCharsRegExp = /[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
+               attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
+               textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,\r
                rawCharsRegExp = /[<>&\"\']/g,\r
                entityRegExp = /&(#x|#)?([\w]+);/g,\r
                asciiMap = {\r
@@ -2118,7 +2267,7 @@ tinymce.html.Styles = function(settings, schema) {
                                '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE\r
                                '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI\r
                                '(?:\\/([^>]+)>)|' + // End element\r
-                               '(?:([^\\s\\/<>]+)\\s*((?:[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*)>)' + // Start element\r
+                               '(?:([^\\s\\/<>]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/)>)' + // Start element\r
                        ')', 'g');\r
 \r
                        attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;\r
@@ -2648,6 +2797,10 @@ tinymce.html.Styles = function(settings, schema) {
                                                }\r
                                        }\r
 \r
+                                       // Keep comments\r
+                                       if (node.type === 8)\r
+                                               return false;\r
+                                       \r
                                        // Keep non whitespace text nodes\r
                                        if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))\r
                                                return false;\r
@@ -3823,52 +3976,59 @@ tinymce.html.Writer = function(settings) {
 \r
                        return this.run(e, function(e) {\r
                                var s = t.settings;\r
+                               var originalValue = e.getAttribute(n);\r
+                               if (v !== null) {\r
+                                       switch (n) {\r
+                                               case "style":\r
+                                                       if (!is(v, 'string')) {\r
+                                                               each(v, function(v, n) {\r
+                                                                       t.setStyle(e, n, v);\r
+                                                               });\r
 \r
-                               switch (n) {\r
-                                       case "style":\r
-                                               if (!is(v, 'string')) {\r
-                                                       each(v, function(v, n) {\r
-                                                               t.setStyle(e, n, v);\r
-                                                       });\r
-\r
-                                                       return;\r
-                                               }\r
+                                                               return;\r
+                                                       }\r
 \r
-                                               // No mce_style for elements with these since they might get resized by the user\r
-                                               if (s.keep_values) {\r
-                                                       if (v && !t._isRes(v))\r
-                                                               e.setAttribute('data-mce-style', v, 2);\r
-                                                       else\r
-                                                               e.removeAttribute('data-mce-style', 2);\r
-                                               }\r
+                                                       // No mce_style for elements with these since they might get resized by the user\r
+                                                       if (s.keep_values) {\r
+                                                               if (v && !t._isRes(v))\r
+                                                                       e.setAttribute('data-mce-style', v, 2);\r
+                                                               else\r
+                                                                       e.removeAttribute('data-mce-style', 2);\r
+                                                       }\r
 \r
-                                               e.style.cssText = v;\r
-                                               break;\r
+                                                       e.style.cssText = v;\r
+                                                       break;\r
 \r
-                                       case "class":\r
-                                               e.className = v || ''; // Fix IE null bug\r
-                                               break;\r
+                                               case "class":\r
+                                                       e.className = v || ''; // Fix IE null bug\r
+                                                       break;\r
 \r
-                                       case "src":\r
-                                       case "href":\r
-                                               if (s.keep_values) {\r
-                                                       if (s.url_converter)\r
-                                                               v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
+                                               case "src":\r
+                                               case "href":\r
+                                                       if (s.keep_values) {\r
+                                                               if (s.url_converter)\r
+                                                                       v = s.url_converter.call(s.url_converter_scope || t, v, n, e);\r
 \r
-                                                       t.setAttrib(e, 'data-mce-' + n, v, 2);\r
-                                               }\r
+                                                               t.setAttrib(e, 'data-mce-' + n, v, 2);\r
+                                                       }\r
 \r
-                                               break;\r
+                                                       break;\r
 \r
-                                       case "shape":\r
-                                               e.setAttribute('data-mce-style', v);\r
-                                               break;\r
+                                               case "shape":\r
+                                                       e.setAttribute('data-mce-style', v);\r
+                                                       break;\r
+                                       }\r
                                }\r
-\r
                                if (is(v) && v !== null && v.length !== 0)\r
                                        e.setAttribute(n, '' + v, 2);\r
                                else\r
                                        e.removeAttribute(n, 2);\r
+\r
+                               // fire onChangeAttrib event for attributes that have changed\r
+                               if (tinyMCE.activeEditor && originalValue != v) {\r
+                                       var ed = tinyMCE.activeEditor;\r
+                                       ed.onSetAttrib.dispatch(ed, e, n, v);\r
+                               }\r
                        });\r
                },\r
 \r
@@ -4512,6 +4672,10 @@ tinymce.html.Writer = function(settings) {
                                                }\r
                                        }\r
 \r
+                                       // Keep comment nodes\r
+                                       if (type == 8)\r
+                                               return false;\r
+\r
                                        // Keep non whitespace text nodes\r
                                        if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))\r
                                                return false;\r
@@ -4574,6 +4738,12 @@ tinymce.html.Writer = function(settings) {
                        function trim(node) {\r
                                var i, children = node.childNodes, type = node.nodeType;\r
 \r
+                               function surroundedBySpans(node) {\r
+                                       var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';\r
+                                       var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';\r
+                                       return previousIsSpan && nextIsSpan;\r
+                               }\r
+\r
                                if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')\r
                                        return;\r
 \r
@@ -4584,7 +4754,10 @@ tinymce.html.Writer = function(settings) {
                                        // Keep non whitespace text nodes\r
                                        if (type == 3 && node.nodeValue.length > 0) {\r
                                                // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"\r
-                                               if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)\r
+                                               // Also keep text nodes with only spaces if surrounded by spans.\r
+                                               // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b\r
+                                               var trimmedLength = tinymce.trim(node.nodeValue).length;\r
+                                               if (!t.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength == 0 && surroundedBySpans(node))\r
                                                        return;\r
                                        } else if (type == 1) {\r
                                                // If the only child is a bookmark then move it up\r
@@ -4621,9 +4794,9 @@ tinymce.html.Writer = function(settings) {
 \r
                                // Insert middle chunk\r
                                if (re)\r
-                                       pa.replaceChild(re, e);\r
-                               else\r
-                                       pa.insertBefore(e, pe);\r
+                               pa.replaceChild(re, e);\r
+                       else\r
+                               pa.insertBefore(e, pe);\r
 \r
                                // Insert after chunk\r
                                pa.insertBefore(trim(aft), pe);\r
@@ -5640,7 +5813,7 @@ tinymce.html.Writer = function(settings) {
                                parent = node.parentNode;\r
                                root = dom.getRoot().parentNode;\r
 \r
-                               while (parent != root) {\r
+                               while (parent != root && parent.nodeType !== 9) {\r
                                        children = parent.children;\r
 \r
                                        i = children.length;\r
@@ -7123,6 +7296,12 @@ window.tinymce.dom.Sizzle = Sizzle;
                                return;\r
                        }\r
 \r
+                       // When loaded asynchronously, the DOM Content may already be loaded\r
+                       if (doc.readyState === 'complete') {\r
+                               t._pageInit(win);\r
+                               return;\r
+                       }\r
+\r
                        // Use IE method\r
                        if (doc.attachEvent) {\r
                                doc.attachEvent("onreadystatechange", function() {\r
@@ -7923,7 +8102,8 @@ window.tinymce.dom.Sizzle = Sizzle;
                                        }\r
 \r
                                        s.addRange(r);\r
-                                       t.selectedRange = s.getRangeAt(0);\r
+                                       // adding range isn't always successful so we need to check range count otherwise an exception can occur\r
+                                       t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;\r
                                }\r
                        } else {\r
                                // Is W3C Range\r
@@ -8019,7 +8199,8 @@ window.tinymce.dom.Sizzle = Sizzle;
                        if (sb && eb && sb != eb) {\r
                                n = sb;\r
 \r
-                               while ((n = n.nextSibling) && n != eb) {\r
+                               var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());\r
+                               while ((n = walker.next()) && n != eb) {\r
                                        if (dom.isBlock(n))\r
                                                bl.push(n);\r
                                }\r
@@ -8034,6 +8215,11 @@ window.tinymce.dom.Sizzle = Sizzle;
                normalize : function() {\r
                        var self = this, rng, normalized;\r
 \r
+                       // TODO:\r
+                       // Retain selection direction.\r
+                       // Lean left/right on Gecko for inline elements.\r
+                       // Run this on mouse up/key up when the user manually moves the selection\r
+                       \r
                        // Normalize only on non IE browsers for now\r
                        if (tinymce.isIE)\r
                                return;\r
@@ -8068,18 +8254,24 @@ window.tinymce.dom.Sizzle = Sizzle;
                                                                if (node.nodeType === 3) {\r
                                                                        offset = start ? 0 : node.nodeValue.length - 1;\r
                                                                        container = node;\r
+                                                                       normalized = true;\r
                                                                        break;\r
                                                                }\r
 \r
-                                                               // Found a BR element that we can place the caret before\r
-                                                               if (node.nodeName === 'BR') {\r
+                                                               // Found a BR/IMG element that we can place the caret before\r
+                                                               if (/^(BR|IMG)$/.test(node.nodeName)) {\r
                                                                        offset = dom.nodeIndex(node);\r
                                                                        container = node.parentNode;\r
+\r
+                                                                       // Put caret after image when moving the end point\r
+                                                                       if (node.nodeName ==  "IMG" && !start) {\r
+                                                                               offset++;\r
+                                                                       }\r
+\r
+                                                                       normalized = true;\r
                                                                        break;\r
                                                                }\r
                                                        } while (node = (start ? walker.next() : walker.prev()));\r
-\r
-                                                       normalized = true;\r
                                                }\r
                                        }\r
                                }\r
@@ -8094,7 +8286,7 @@ window.tinymce.dom.Sizzle = Sizzle;
                        // Normalize the end points\r
                        normalizeEndPoint(true);\r
                        \r
-                       if (rng.collapsed)\r
+                       if (!rng.collapsed)\r
                                normalizeEndPoint();\r
 \r
                        // Set the selection if it was normalized\r
@@ -8206,12 +8398,11 @@ window.tinymce.dom.Sizzle = Sizzle;
                if (!settings.apply_source_formatting)\r
                        settings.indent = false;\r
 \r
-               settings.remove_trailing_brs = true;\r
-\r
                // Default DOM and Schema if they are undefined\r
                dom = dom || tinymce.DOM;\r
                schema = schema || new tinymce.html.Schema(settings);\r
                settings.entity_encoding = settings.entity_encoding || 'named';\r
+               settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;\r
 \r
                onPreProcess = new tinymce.util.Dispatcher(self);\r
 \r
@@ -8275,8 +8466,8 @@ window.tinymce.dom.Sizzle = Sizzle;
                        function trim(value) {\r
                                return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')\r
                                                .replace(/^[\r\n]*|[\r\n]*$/g, '')\r
-                                               .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')\r
-                                               .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');\r
+                                               .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')\r
+                                               .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');\r
                        };\r
 \r
                        while (i--) {\r
@@ -8425,7 +8616,7 @@ window.tinymce.dom.Sizzle = Sizzle;
 \r
                                // Replace all BOM characters for now until we can find a better solution\r
                                if (!args.cleanup)\r
-                                       args.content = args.content.replace(/\uFEFF/g, '');\r
+                                       args.content = args.content.replace(/\uFEFF|\u200B/g, '');\r
 \r
                                // Post process\r
                                if (!args.no_events)\r
@@ -8719,6 +8910,24 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                                return;\r
                        }\r
 \r
+                       function exclude(nodes) {\r
+                               var node;\r
+\r
+                               // First node is excluded\r
+                               node = nodes[0];\r
+                               if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {\r
+                                       nodes.splice(0, 1);\r
+                               }\r
+\r
+                               // Last node is excluded\r
+                               node = nodes[nodes.length - 1];\r
+                               if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {\r
+                                       nodes.splice(nodes.length - 1, 1);\r
+                               }\r
+\r
+                               return nodes;\r
+                       };\r
+\r
                        function collectSiblings(node, name, end_node) {\r
                                var siblings = [];\r
 \r
@@ -8748,7 +8957,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                                                if (!next)\r
                                                        siblings.reverse();\r
 \r
-                                               callback(siblings);\r
+                                               callback(exclude(siblings));\r
                                        }\r
                                }\r
                        };\r
@@ -8761,28 +8970,28 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                        if (endContainer.nodeType == 1 && endContainer.hasChildNodes())\r
                                endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];\r
 \r
-                       // Find common ancestor and end points\r
-                       ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
-\r
                        // Same container\r
                        if (startContainer == endContainer)\r
-                               return callback([startContainer]);\r
+                               return callback(exclude([startContainer]));\r
 \r
+                       // Find common ancestor and end points\r
+                       ancestor = dom.findCommonAncestor(startContainer, endContainer);\r
+                               \r
                        // Process left side\r
                        for (node = startContainer; node; node = node.parentNode) {\r
-                               if (node == endContainer)\r
+                               if (node === endContainer)\r
                                        return walkBoundary(startContainer, ancestor, true);\r
 \r
-                               if (node == ancestor)\r
+                               if (node === ancestor)\r
                                        break;\r
                        }\r
 \r
                        // Process right side\r
                        for (node = endContainer; node; node = node.parentNode) {\r
-                               if (node == startContainer)\r
+                               if (node === startContainer)\r
                                        return walkBoundary(endContainer, ancestor);\r
 \r
-                               if (node == ancestor)\r
+                               if (node === ancestor)\r
                                        break;\r
                        }\r
 \r
@@ -8801,48 +9010,46 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                        );\r
 \r
                        if (siblings.length)\r
-                               callback(siblings);\r
+                               callback(exclude(siblings));\r
 \r
                        // Walk right leaf\r
                        walkBoundary(endContainer, endPoint);\r
                };\r
 \r
-               /*              this.split = function(rng) {\r
+               this.split = function(rng) {\r
                        var startContainer = rng.startContainer,\r
                                startOffset = rng.startOffset,\r
                                endContainer = rng.endContainer,\r
                                endOffset = rng.endOffset;\r
 \r
                        function splitText(node, offset) {\r
-                               if (offset == node.nodeValue.length)\r
-                                       node.appendData(INVISIBLE_CHAR);\r
-\r
-                               node = node.splitText(offset);\r
-\r
-                               if (node.nodeValue === INVISIBLE_CHAR)\r
-                                       node.nodeValue = '';\r
-\r
-                               return node;\r
+                               return node.splitText(offset);\r
                        };\r
 \r
                        // Handle single text node\r
-                       if (startContainer == endContainer) {\r
-                               if (startContainer.nodeType == 3) {\r
-                                       if (startOffset != 0)\r
-                                               startContainer = endContainer = splitText(startContainer, startOffset);\r
-\r
-                                       if (endOffset - startOffset != startContainer.nodeValue.length)\r
-                                               splitText(startContainer, endOffset - startOffset);\r
+                       if (startContainer == endContainer && startContainer.nodeType == 3) {\r
+                               if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
+                                       endContainer = splitText(startContainer, startOffset);\r
+                                       startContainer = endContainer.previousSibling;\r
+\r
+                                       if (endOffset > startOffset) {\r
+                                               endOffset = endOffset - startOffset;\r
+                                               startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;\r
+                                               endOffset = endContainer.nodeValue.length;\r
+                                               startOffset = 0;\r
+                                       } else {\r
+                                               endOffset = 0;\r
+                                       }\r
                                }\r
                        } else {\r
                                // Split startContainer text node if needed\r
-                               if (startContainer.nodeType == 3 && startOffset != 0) {\r
+                               if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {\r
                                        startContainer = splitText(startContainer, startOffset);\r
                                        startOffset = 0;\r
                                }\r
 \r
                                // Split endContainer text node if needed\r
-                               if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {\r
+                               if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {\r
                                        endContainer = splitText(endContainer, endOffset).previousSibling;\r
                                        endOffset = endContainer.nodeValue.length;\r
                                }\r
@@ -8855,7 +9062,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                                endOffset : endOffset\r
                        };\r
                };\r
-*/\r
+\r
        };\r
 \r
        tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {\r
@@ -9025,6 +9232,7 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
                }\r
        });\r
 })(tinymce);\r
+\r
 (function(tinymce) {\r
        // Shorten class names\r
        var DOM = tinymce.DOM, is = tinymce.is;\r
@@ -9560,7 +9768,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                // Internal functions\r
                _setupKeyboardNav : function(){\r
                        var contextMenu, menuItems, t=this; \r
-                       contextMenu = DOM.select('#menu_' + t.id)[0];\r
+                       contextMenu = DOM.get('menu_' + t.id);\r
                        menuItems = DOM.select('a[role=option]', 'menu_' + t.id);\r
                        menuItems.splice(0,0,contextMenu);\r
                        t.keyboardNav = new tinymce.ui.KeyboardNavigation({\r
@@ -9667,10 +9875,27 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                },\r
 \r
                postRender : function() {\r
-                       var t = this, s = t.settings;\r
-\r
+                       var t = this, s = t.settings, imgBookmark;\r
+\r
+                       // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so\r
+                       // need to keep the selection in case the selection is lost\r
+                       if (tinymce.isIE && t.editor) {\r
+                               tinymce.dom.Event.add(t.id, 'mousedown', function(e) {\r
+                                       var nodeName = t.editor.selection.getNode().nodeName;\r
+                                       imgBookmark = nodeName === 'IMG' ? t.editor.selection.getBookmark() : null;\r
+                               });\r
+                       }\r
                        tinymce.dom.Event.add(t.id, 'click', function(e) {\r
-                               if (!t.isDisabled())\r
+                               if (!t.isDisabled()) {\r
+                                       // restore the selection in case the selection is lost in IE\r
+                                       if (tinymce.isIE && t.editor && imgBookmark !== null) {\r
+                                               t.editor.selection.moveToBookmark(imgBookmark);\r
+                                       }\r
+                                       return s.onclick.call(s.scope, e);\r
+                               }\r
+                       });\r
+                       tinymce.dom.Event.add(t.id, 'keyup', function(e) {\r
+                               if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR)\r
                                        return s.onclick.call(s.scope, e);\r
                        });\r
                }\r
@@ -9706,7 +9931,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                                return t.selectByIndex(-1);\r
 \r
                        // Is string or number make function selector\r
-                       if (va && va.call)\r
+                       if (va && typeof(va)=="function")\r
                                f = va;\r
                        else {\r
                                f = function(v) {\r
@@ -9731,20 +9956,23 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                },\r
 \r
                selectByIndex : function(idx) {\r
-                       var t = this, e, o;\r
+                       var t = this, e, o, label;\r
 \r
                        if (idx != t.selectedIndex) {\r
                                e = DOM.get(t.id + '_text');\r
+                               label = DOM.get(t.id + '_voiceDesc');\r
                                o = t.items[idx];\r
 \r
                                if (o) {\r
                                        t.selectedValue = o.value;\r
                                        t.selectedIndex = idx;\r
                                        DOM.setHTML(e, DOM.encode(o.title));\r
+                                       DOM.setHTML(label, t.settings.title + " - " + o.title);\r
                                        DOM.removeClass(e, 'mceTitle');\r
                                        DOM.setAttrib(t.id, 'aria-valuenow', o.title);\r
                                } else {\r
                                        DOM.setHTML(e, DOM.encode(t.settings.title));\r
+                                       DOM.setHTML(label, DOM.encode(t.settings.title));\r
                                        DOM.addClass(e, 'mceTitle');\r
                                        t.selectedValue = t.selectedIndex = null;\r
                                        DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title);\r
@@ -9773,7 +10001,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                renderHTML : function() {\r
                        var h = '', t = this, s = t.settings, cp = t.classPrefix;\r
 \r
-                       h = '<span role="button" aria-haspopup="true" aria-labelledby="' + t.id +'_text" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
+                       h = '<span role="listbox" aria-haspopup="true" aria-labelledby="' + t.id +'_voiceDesc" aria-describedby="' + t.id + '_voiceDesc"><table role="presentation" tabindex="0" id="' + t.id + '" cellpadding="0" cellspacing="0" class="' + cp + ' ' + cp + 'Enabled' + (s['class'] ? (' ' + s['class']) : '') + '"><tbody><tr>';\r
                        h += '<td>' + DOM.createHTML('span', {id: t.id + '_voiceDesc', 'class': 'voiceLabel', style:'display:none;'}, t.settings.title); \r
                        h += DOM.createHTML('a', {id : t.id + '_text', tabindex : -1, href : 'javascript:;', 'class' : 'mceText', onclick : "return false;", onmousedown : 'return false;'}, DOM.encode(t.settings.title)) + '</td>';\r
                        h += '<td>' + DOM.createHTML('a', {id : t.id + '_open', tabindex : -1, href : 'javascript:;', 'class' : 'mceOpen', onclick : "return false;", onmousedown : 'return false;'}, '<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>') + '</td>';\r
@@ -9869,6 +10097,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                                if (o.value === undefined) {\r
                                        m.add({\r
                                                title : o.title,\r
+                                               role : "option",\r
                                                'class' : 'mceMenuItemTitle',\r
                                                onclick : function() {\r
                                                        if (t.settings.onselect('') !== false)\r
@@ -9877,6 +10106,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                                        });\r
                                } else {\r
                                        o.id = DOM.uniqueId();\r
+                                       o.role= "option";\r
                                        o.onclick = function() {\r
                                                if (t.settings.onselect(o.value) !== false)\r
                                                        t.select(o.value); // Must be runned after\r
@@ -9952,6 +10182,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
@@ -9977,7 +10208,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                                return t.selectByIndex(-1);\r
 \r
                        // Is string or number make function selector\r
-                       if (va && va.call)\r
+                       if (va && typeof(va)=="function")\r
                                f = va;\r
                        else {\r
                                f = function(v) {\r
@@ -10210,8 +10441,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                        h += '<td >' + DOM.createHTML('a', {role: 'button', id : t.id + '_open', tabindex: '-1', href : 'javascript:;', 'class' : 'mceOpen ' + s['class'], onclick : "return false;", onmousedown : 'return false;', title : s.title}, h1) + '</td>';\r
 \r
                        h += '</tr></tbody>';\r
-                       h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0',  'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
-                       return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
+                       h = DOM.createHTML('table', { role: 'presentation',   'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h);\r
+                       return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h);\r
                },\r
 \r
                postRender : function() {\r
@@ -10371,15 +10602,21 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
                                }\r
 \r
                                n = DOM.add(tr, 'td');\r
-                               n = DOM.add(n, 'a', {\r
-                                       role : 'option',\r
+                               var settings = {\r
                                        href : 'javascript:;',\r
                                        style : {\r
                                                backgroundColor : '#' + c\r
                                        },\r
                                        'title': t.editor.getLang('colors.' + c, c),\r
                                        'data-mce-color' : '#' + c\r
-                               });\r
+                               };\r
+\r
+                               // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.\r
+                               if (!tinymce.isIE ) {\r
+                                       settings['role']= 'option';\r
+                               }\r
+\r
+                               n = DOM.add(n, 'a', settings);\r
 \r
                                if (t.editor.forcedHighContrastMode) {\r
                                        n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });\r
@@ -10485,7 +10722,8 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {
        },\r
        \r
        focus : function() {\r
-               this.keyNav.focus();\r
+               var t = this;\r
+               dom.get(t.id).focus();\r
        },\r
        \r
        postRender : function() {\r
@@ -10503,6 +10741,10 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', {
                        root: t.id,\r
                        items: items,\r
                        onCancel: function() {\r
+                               //Move focus if webkit so that navigation back will read the item.\r
+                               if (tinymce.isWebKit) {\r
+                                       dom.get(t.editor.id+"_ifr").focus();\r
+                               }\r
                                t.editor.focus();\r
                        },\r
                        excludeFromTabOrder: !t.settings.tab_focus_toolbar\r
@@ -11010,7 +11252,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,\r
                isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,\r
                ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,\r
-               inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;\r
+               inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode, VK = tinymce.VK;\r
 \r
        tinymce.create('tinymce.Editor', {\r
                Editor : function(id, s) {\r
@@ -11034,6 +11276,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                'onPostRender',\r
 \r
+                               'onLoad',\r
+\r
                                'onInit',\r
 \r
                                'onRemove',\r
@@ -11096,7 +11340,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                'onVisualAid',\r
 \r
-                               'onSetProgressState'\r
+                               'onSetProgressState',\r
+\r
+                               'onSetAttrib'\r
                        ], function(e) {\r
                                t[e] = new Dispatcher(t);\r
                        });\r
@@ -11130,6 +11376,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                visual_table_class : 'mceItemTable',\r
                                visual : 1,\r
                                font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',\r
+                               font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size\r
                                apply_source_formatting : 1,\r
                                directionality : 'ltr',\r
                                forced_root_block : 'p',\r
@@ -11430,6 +11677,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';\r
 \r
+                       // Load the CSS by injecting them into the HTML this will reduce "flicker"\r
+                       for (i = 0; i < t.contentCSS.length; i++) {\r
+                               t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';\r
+                       }\r
+\r
+                       t.contentCSS = [];\r
+\r
                        bi = s.body_id || 'tinymce';\r
                        if (bi.indexOf('=') != -1) {\r
                                bi = t.getParam('body_id', '', 'hash');\r
@@ -11442,12 +11696,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                bc = bc[t.id] || '';\r
                        }\r
 \r
-                       t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';\r
+                       t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';\r
 \r
                        // Domain relaxing enabled, then set document domain\r
                        if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {\r
                                // We need to write the contents here in IE since multiple writes messes up refresh button and back button\r
-                               u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';                         \r
+                               u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';\r
                        }\r
 \r
                        // Create iframe\r
@@ -11481,24 +11735,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        // Setup iframe body\r
                        if (!isIE || !tinymce.relaxedDomain) {\r
-                               // Fix for a focus bug in FF 3.x where the body element\r
-                               // wouldn't get proper focus if the user clicked on the HTML element\r
-                               if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4\r
-                                       t.onMouseDown.add(function(ed, e) {\r
-                                               if (e.target.nodeName === "HTML") {\r
-                                                       var body = t.getBody();\r
-\r
-                                                       // Blur the body it's focused but not correctly focused\r
-                                                       body.blur();\r
-\r
-                                                       // Refocus the body after a little while\r
-                                                       setTimeout(function() {\r
-                                                               body.focus();\r
-                                                       }, 0);\r
-                                               }\r
-                                       });\r
-                               }\r
-\r
                                d.open();\r
                                d.write(t.iframeHTML);\r
                                d.close();\r
@@ -11574,10 +11810,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        // Keep scripts from executing\r
                        t.parser.addNodeFilter('script', function(nodes, name) {\r
-                               var i = nodes.length;\r
+                               var i = nodes.length, node;\r
 \r
-                               while (i--)\r
-                                       nodes[i].attr('type', 'mce-text/javascript');\r
+                               while (i--) {\r
+                                       node = nodes[i];\r
+                                       node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));\r
+                               }\r
                        });\r
 \r
                        t.parser.addNodeFilter('#cdata', function(nodes, name) {\r
@@ -11926,7 +12164,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
 \r
                                t._refreshContentEditable();\r
-                               selection.normalize();\r
 \r
                                // Is not content editable\r
                                if (!ce)\r
@@ -12126,9 +12363,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))\r
                                t.focus();\r
 \r
-                       o = {};\r
-                       t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);\r
-                       if (o.terminate)\r
+                       a = extend({}, a);\r
+                       t.onBeforeExecCommand.dispatch(t, cmd, ui, val, a);\r
+                       if (a.terminate)\r
                                return false;\r
 \r
                        // Command callback\r
@@ -12685,21 +12922,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                t.onMouseDown.add(setOpts);\r
                        }\r
 \r
-                       t.onClick.add(function(ed, e) {\r
-                               e = e.target;\r
-\r
-                               // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250\r
-                               // WebKit can't even do simple things like selecting an image\r
-                               // Needs tobe the setBaseAndExtend or it will fail to select floated images\r
-                               if (tinymce.isWebKit && e.nodeName == 'IMG')\r
-                                       t.selection.getSel().setBaseAndExtent(e, 0, e, 1);\r
-\r
-                               if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor'))\r
-                                       t.selection.select(e);\r
-\r
-                               t.nodeChanged();\r
-                       });\r
-\r
                        // Add node change handlers\r
                        t.onMouseUp.add(t.nodeChanged);\r
                        //t.onClick.add(t.nodeChanged);\r
@@ -12713,30 +12935,32 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        // Add block quote deletion handler\r
                        t.onKeyDown.add(function(ed, e) {\r
-                               // Was the BACKSPACE key pressed?\r
-                               if (e.keyCode != 8)\r
+                               if (e.keyCode != VK.BACKSPACE)\r
+                                       return;\r
+\r
+                               var rng = ed.selection.getRng();\r
+                               if (!rng.collapsed)\r
                                        return;\r
 \r
-                               var n = ed.selection.getRng().startContainer;\r
-                               var offset = ed.selection.getRng().startOffset;\r
+                               var n = rng.startContainer;\r
+                               var offset = rng.startOffset;\r
 \r
                                while (n && n.nodeType && n.nodeType != 1 && n.parentNode)\r
                                        n = n.parentNode;\r
-                                       \r
+\r
                                // Is the cursor at the beginning of a blockquote?\r
                                if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {\r
                                        // Remove the blockquote\r
                                        ed.formatter.toggle('blockquote', null, n.parentNode);\r
 \r
                                        // Move the caret to the beginning of n\r
-                                       var rng = ed.selection.getRng();\r
                                        rng.setStart(n, 0);\r
                                        rng.setEnd(n, 0);\r
                                        ed.selection.setRng(rng);\r
                                        ed.selection.collapse(false);\r
                                }\r
                        });\r
\r
+\r
 \r
 \r
                        // Add reset handler\r
@@ -12873,7 +13097,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                        t.undoManager.add();\r
                                };\r
 \r
-                               dom.bind(t.getDoc(), 'focusout', function(e) {\r
+                               var focusLostFunc = tinymce.isGecko ? 'blur' : 'focusout';\r
+                               dom.bind(t.getDoc(), focusLostFunc, function(e){\r
                                        if (!t.removed && t.undoManager.typing)\r
                                                addUndo();\r
                                });\r
@@ -12933,21 +13158,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                });\r
                        }\r
 \r
-                       // Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5\r
-                       // It only fires the nodeChange event every 50ms since it would other wise update the UI when you type and it hogs the CPU\r
-                       if (tinymce.isWebKit) {\r
-                               dom.bind(t.getDoc(), 'selectionchange', function() {\r
-                                       if (t.selectionTimer) {\r
-                                               clearTimeout(t.selectionTimer);\r
-                                               t.selectionTimer = 0;\r
-                                       }\r
-\r
-                                       t.selectionTimer = window.setTimeout(function() {\r
-                                               t.nodeChanged();\r
-                                       }, 50);\r
-                               });\r
-                       }\r
-\r
                        // Bug fix for FireFox keeping styles from end of selection instead of start.\r
                        if (tinymce.isGecko) {\r
                                function getAttributeApplyFunction() {\r
@@ -12957,11 +13167,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                var target = t.selection.getStart();\r
 \r
                                                if (target !== t.getBody()) {\r
-                                                       t.dom.removeAllAttribs(target);\r
+                                                       t.dom.setAttrib(target, "style", null);\r
 \r
-                                                       each(template, function(attr) {\r
-                                                               target.setAttributeNode(attr.cloneNode(true));\r
-                                                       });\r
+                                               each(template, function(attr) {\r
+                                                       target.setAttributeNode(attr.cloneNode(true));\r
+                                               });\r
                                                }\r
                                        };\r
                                }\r
@@ -13273,6 +13483,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                var parser, serializer, parentNode, rootNode, fragment, args,\r
                                        marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;\r
 \r
+                               //selection.normalize();\r
+\r
                                // Setup parser and serializer\r
                                parser = editor.parser;\r
                                serializer = new tinymce.html.Serializer({}, editor.schema);\r
@@ -13499,7 +13711,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                addCommands({\r
                        // Override justify commands\r
                        'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {\r
-                               return isFormatMatch('align' + command.substring(7));\r
+                               var name = 'align' + command.substring(7);\r
+                               // Use Formatter.matchNode instead of Formatter.match so that we don't match on parent node. This fixes bug where for both left\r
+                               // and right align buttons can be active. This could occur when selected nodes have align right and the parent has align left.\r
+                               var nodes = selection.isCollapsed() ? [selection.getNode()] : selection.getSelectedBlocks();\r
+                               var matches = tinymce.map(nodes, function(node) {\r
+                                       return !!formatter.matchNode(node, name);\r
+                               });\r
+                               return tinymce.inArray(matches, TRUE) !== -1;\r
                        },\r
 \r
                        'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {\r
@@ -14456,7 +14675,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        id = t.prefix + id;\r
 \r
-                       if (ed.settings.use_native_selects)\r
+\r
+                       function useNativeListForAccessibility(ed) {\r
+                               return ed.settings.use_accessible_selects && !tinymce.isGecko\r
+                       }\r
+\r
+                       if (ed.settings.use_native_selects || useNativeListForAccessibility(ed))\r
                                c = new tinymce.ui.NativeListBox(id, s);\r
                        else {\r
                                cls = cc || t._cls.listbox || tinymce.ui.ListBox;\r
@@ -14805,8 +15029,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        MCE_ATTR_RE = /^(src|href|style)$/,\r
                        FALSE = false,\r
                        TRUE = true,\r
-                       undefined,\r
-                       pendingFormats = {apply : [], remove : []};\r
+                       undefined;\r
 \r
                function isArray(obj) {\r
                        return obj instanceof Array;\r
@@ -14817,7 +15040,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function isCaretNode(node) {\r
-                       return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');\r
+                       return node.nodeType === 1 && node.id === '_mce_caret';\r
                };\r
 \r
                // Public functions\r
@@ -14892,30 +15115,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                function apply(name, vars, node) {\r
                        var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();\r
 \r
-                       function moveStart(rng) {\r
-                               var container = rng.startContainer,\r
-                                       offset = rng.startOffset,\r
-                                       walker, node;\r
-\r
-                               // Move startContainer/startOffset in to a suitable node\r
-                               if (container.nodeType == 1 || container.nodeValue === "") {\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
-\r
-                               return rng;\r
-                       };\r
-\r
                        function setElementFormat(elm, fmt) {\r
                                fmt = fmt || format;\r
 \r
@@ -15022,7 +15221,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                }\r
                        };\r
                        \r
-                       function applyRngStyle(rng, bookmark) {\r
+                       function applyRngStyle(rng, bookmark, node_specific) {\r
                                var newWrappers = [], wrapName, wrapElm;\r
 \r
                                // Setup wrapper element\r
@@ -15086,7 +15285,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                                                // Is it valid to wrap this item\r
                                                if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&\r
-                                                               !(node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279)) {\r
+                                                               !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && !isCaretNode(node)) {\r
                                                        // Start wrapping\r
                                                        if (!currentWrapElm) {\r
                                                                // Wrap the node\r
@@ -15241,12 +15440,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        if (format) {\r
                                if (node) {\r
-                                       rng = dom.createRng();\r
-\r
-                                       rng.setStartBefore(node);\r
-                                       rng.setEndAfter(node);\r
-\r
-                                       applyRngStyle(expandRng(rng, formatList));\r
+                                       if (node.nodeType) {\r
+                                               rng = dom.createRng();\r
+                                               rng.setStartBefore(node);\r
+                                               rng.setEndAfter(node);\r
+                                               applyRngStyle(expandRng(rng, formatList), null, true);\r
+                                       } else {\r
+                                               applyRngStyle(node, null, true);\r
+                                       }\r
                                } else {\r
                                        if (!isCollapsed || !format.inline || dom.select('td.mceSelected,th.mceSelected').length) {\r
                                                // Obtain selection node before selection is unselected by applyRngStyle()\r
@@ -15264,7 +15465,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                }\r
 \r
                                                selection.moveToBookmark(bookmark);\r
-                                               selection.setRng(moveStart(selection.getRng(TRUE)));\r
+                                               moveStart(selection.getRng(TRUE));\r
                                                ed.nodeChanged();\r
                                        } else\r
                                                performCaretAction('apply', name, vars);\r
@@ -15274,44 +15475,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                function remove(name, vars, node) {\r
                        var formatList = get(name), format = formatList[0], bookmark, i, rng;\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
@@ -15460,10 +15623,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
 \r
                        // Handle node\r
                        if (node) {\r
-                               rng = dom.createRng();\r
-                               rng.setStartBefore(node);\r
-                               rng.setEndAfter(node);\r
-                               removeRngStyle(rng);\r
+                               if (node.nodeType) {\r
+                                       rng = dom.createRng();\r
+                                       rng.setStartBefore(node);\r
+                                       rng.setEndAfter(node);\r
+                                       removeRngStyle(rng);\r
+                               } else {\r
+                                       removeRngStyle(node);\r
+                               }\r
+\r
                                return;\r
                        }\r
 \r
@@ -15473,13 +15641,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                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
+                               if (format.inline && match(name, vars, selection.getStart())) {\r
                                        moveStart(selection.getRng(true));\r
                                }\r
 \r
                                ed.nodeChanged();\r
                        } else\r
                                performCaretAction('remove', name, vars);\r
+\r
+                       // When you remove formatting from a table cell in WebKit (cell, not the contents of a cell) there is a rendering issue with column width\r
+                       if (tinymce.isWebKit) {\r
+                               ed.execCommand('mceCleanup');\r
+                       }\r
                };\r
 \r
                function toggle(name, vars, node) {\r
@@ -15554,7 +15727,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function match(name, vars, node) {\r
-                       var startNode, i;\r
+                       var startNode;\r
 \r
                        function matchParents(node) {\r
                                // Find first node with similar format settings\r
@@ -15570,21 +15743,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        if (node)\r
                                return matchParents(node);\r
 \r
-                       // Check pending formats\r
-                       if (selection.isCollapsed()) {\r
-                               for (i = pendingFormats.apply.length - 1; i >= 0; i--) {\r
-                                       if (pendingFormats.apply[i].name == name)\r
-                                               return true;\r
-                               }\r
-\r
-                               for (i = pendingFormats.remove.length - 1; i >= 0; i--) {\r
-                                       if (pendingFormats.remove[i].name == name)\r
-                                               return false;\r
-                               }\r
-\r
-                               return matchParents(selection.getNode());\r
-                       }\r
-\r
                        // Check selected node\r
                        node = selection.getNode();\r
                        if (matchParents(node))\r
@@ -15603,33 +15761,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                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
@@ -15738,7 +15869,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function isWhiteSpaceNode(node) {\r
-                       return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);\r
+                       return node && node.nodeType === 3 && /^([\\r\n]+|)$/.test(node.nodeValue);\r
                };\r
 \r
                function wrap(node, name, attrs) {\r
@@ -15754,31 +15885,42 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        var startContainer = rng.startContainer,\r
                                startOffset = rng.startOffset,\r
                                endContainer = rng.endContainer,\r
-                               endOffset = rng.endOffset, sibling, lastIdx, leaf;\r
+                               endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;\r
 \r
                        // This function walks up the tree if there is no siblings before/after the node\r
-                       function findParentContainer(container, child_name, sibling_name, root) {\r
-                               var parent, child;\r
-\r
-                               root = root || dom.getRoot();\r
+                       function findParentContainer(start) {\r
+                               var container, parent, child, sibling, siblingName;\r
 \r
-                               for (;;) {\r
-                                       // Check if we can move up are we at root level or body level\r
-                                       parent = container.parentNode;\r
+                               container = parent = start ? startContainer : endContainer;\r
+                               siblingName = start ? 'previousSibling' : 'nextSibling';\r
+                               root = dom.getRoot();\r
 \r
-                                       // Stop expanding on block elements or root depending on format\r
-                                       if (parent == root || (!format[0].block_expand && isBlock(parent)))\r
+                               // If it's a text node and the offset is inside the text\r
+                               if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {\r
+                                       if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {\r
                                                return container;\r
+                                       }\r
+                               }\r
 \r
-                                       for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) {\r
-                                               if (sibling.nodeType == 1 && !isBookmarkNode(sibling))\r
-                                                       return container;\r
+                               for (;;) {\r
+                                       // Stop expanding on block elements\r
+                                       if (!format[0].block_expand && isBlock(parent))\r
+                                               return parent;\r
+\r
+                                       // Walk left/right\r
+                                       for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {\r
+                                               if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling)) {\r
+                                                       return parent;\r
+                                               }\r
+                                       }\r
 \r
-                                               if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling))\r
-                                                       return container;\r
+                                       // Check if we can move up are we at root level or body level\r
+                                       if (parent.parentNode == root) {\r
+                                               container = parent;\r
+                                               break;\r
                                        }\r
 \r
-                                       container = container.parentNode;\r
+                                       parent = parent.parentNode;\r
                                }\r
 \r
                                return container;\r
@@ -15816,23 +15958,103 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                        }\r
 \r
                        // Exclude bookmark nodes if possible\r
-                       if (isBookmarkNode(startContainer.parentNode))\r
-                               startContainer = startContainer.parentNode;\r
-\r
-                       if (isBookmarkNode(startContainer))\r
+                       if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {\r
+                               startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;\r
                                startContainer = startContainer.nextSibling || startContainer;\r
 \r
-                       if (isBookmarkNode(endContainer.parentNode)) {\r
-                               endOffset = dom.nodeIndex(endContainer);\r
-                               endContainer = endContainer.parentNode;\r
+                               if (startContainer.nodeType == 3)\r
+                                       startOffset = 0;\r
                        }\r
 \r
-                       if (isBookmarkNode(endContainer) && endContainer.previousSibling) {\r
-                               endContainer = endContainer.previousSibling;\r
-                               endOffset = endContainer.length;\r
+                       if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {\r
+                               endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;\r
+                               endContainer = endContainer.previousSibling || endContainer;\r
+\r
+                               if (endContainer.nodeType == 3)\r
+                                       endOffset = endContainer.length;\r
                        }\r
 \r
                        if (format[0].inline) {\r
+                               if (rng.collapsed) {\r
+                                       function findWordEndPoint(container, offset, start) {\r
+                                               var walker, node, pos, lastTextNode;\r
+\r
+                                               function findSpace(node, offset) {\r
+                                                       var pos, pos2, str = node.nodeValue;\r
+\r
+                                                       if (typeof(offset) == "undefined") {\r
+                                                               offset = start ? str.length : 0;\r
+                                                       }\r
+\r
+                                                       if (start) {\r
+                                                               pos = str.lastIndexOf(' ', offset);\r
+                                                               pos2 = str.lastIndexOf('\u00a0', offset);\r
+                                                               pos = pos > pos2 ? pos : pos2;\r
+\r
+                                                               // Include the space on remove to avoid tag soup\r
+                                                               if (pos !== -1 && !remove) {\r
+                                                                       pos++;\r
+                                                               }\r
+                                                       } else {\r
+                                                               pos = str.indexOf(' ', offset);\r
+                                                               pos2 = str.indexOf('\u00a0', offset);\r
+                                                               pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;\r
+                                                       }\r
+\r
+                                                       return pos;\r
+                                               };\r
+\r
+                                               if (container.nodeType === 3) {\r
+                                                       pos = findSpace(container, offset);\r
+\r
+                                                       if (pos !== -1) {\r
+                                                               return {container : container, offset : pos};\r
+                                                       }\r
+\r
+                                                       lastTextNode = container;\r
+                                               }\r
+\r
+                                               // Walk the nodes inside the block\r
+                                               walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());\r
+                                               while (node = walker[start ? 'prev' : 'next']()) {\r
+                                                       if (node.nodeType === 3) {\r
+                                                               lastTextNode = node;\r
+                                                               pos = findSpace(node);\r
+\r
+                                                               if (pos !== -1) {\r
+                                                                       return {container : node, offset : pos};\r
+                                                               }\r
+                                                       } else if (isBlock(node)) {\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (lastTextNode) {\r
+                                                       if (start) {\r
+                                                               offset = 0;\r
+                                                       } else {\r
+                                                               offset = lastTextNode.length;\r
+                                                       }\r
+\r
+                                                       return {container: lastTextNode, offset: offset};\r
+                                               }\r
+                                       }\r
+\r
+                                       // Expand left to closest word boundery\r
+                                       endPoint = findWordEndPoint(startContainer, startOffset, true);\r
+                                       if (endPoint) {\r
+                                               startContainer = endPoint.container;\r
+                                               startOffset = endPoint.offset;\r
+                                       }\r
+\r
+                                       // Expand right to closest word boundery\r
+                                       endPoint = findWordEndPoint(endContainer, endOffset);\r
+                                       if (endPoint) {\r
+                                               endContainer = endPoint.container;\r
+                                               endOffset = endPoint.offset;\r
+                                       }\r
+                               }\r
+\r
                                // Avoid applying formatting to a trailing space.\r
                                leaf = findLeaf(endContainer, endOffset);\r
                                if (leaf.node) {\r
@@ -15846,19 +16068,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                                        endContainer = leaf.node;\r
                                                        endContainer.splitText(leaf.offset - 1);\r
                                                } else if (leaf.node.previousSibling) {\r
-                                                       endContainer = leaf.node.previousSibling;\r
+                                                       // TODO: Figure out why this is in here\r
+                                                       //endContainer = leaf.node.previousSibling;\r
                                                }\r
                                        }\r
                                }\r
                        }\r
-                       \r
+\r
                        // Move start/end point up the tree if the leaves are sharp and if we are in different containers\r
                        // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>!\r
                        // This will reduce the number of wrapper elements that needs to be created\r
                        // Move start point up the tree\r
                        if (format[0].inline || format[0].block_expand) {\r
-                               startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
-                               endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
+                               if (!format[0].inline || (startContainer.nodeType != 3 || startOffset === 0)) {\r
+                                       startContainer = findParentContainer(true);\r
+                               }\r
+\r
+                               if (!format[0].inline || (endContainer.nodeType != 3 || endOffset === endContainer.nodeValue.length)) {\r
+                                       endContainer = findParentContainer();\r
+                               }\r
                        }\r
 \r
                        // Expand start/end container to matching selector\r
@@ -15932,10 +16160,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                // Non block element then try to expand up the leaf\r
                                if (format[0].block) {\r
                                        if (!isBlock(startContainer))\r
-                                               startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling');\r
+                                               startContainer = findParentContainer(true);\r
 \r
                                        if (!isBlock(endContainer))\r
-                                               endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling');\r
+                                               endContainer = findParentContainer();\r
                                }\r
                        }\r
 \r
@@ -16228,7 +16456,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                };\r
 \r
                function getContainer(rng, start) {\r
-                       var container, offset, lastIdx;\r
+                       var container, offset, lastIdx, walker;\r
 \r
                        container = rng[start ? 'startContainer' : 'endContainer'];\r
                        offset = rng[start ? 'startOffset' : 'endOffset'];\r
@@ -16242,142 +16470,318 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
                                container = container.childNodes[offset > lastIdx ? lastIdx : offset];\r
                        }\r
 \r
+                       // If start text node is excluded then walk to the next node\r
+                       if (container.nodeType === 3 && start && offset >= container.nodeValue.length) {\r
+                               container = new TreeWalker(container, ed.getBody()).next() || container;\r
+                       }\r
+\r
+                       // If end text node is excluded then walk to the previous node\r
+                       if (container.nodeType === 3 && !start && offset == 0) {\r
+                               container = new TreeWalker(container, ed.getBody()).prev() || container;\r
+                       }\r
+\r
                        return container;\r
                };\r
 \r
                function performCaretAction(type, name, vars) {\r
-                       var i, currentPendingFormats = pendingFormats[type],\r
-                               otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply'];\r
+                       var invisibleChar, caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;\r
+\r
+                       // Setup invisible character use zero width space on Gecko since it doesn't change the heigt of the container\r
+                       invisibleChar = tinymce.isGecko ? '\u200B' : INVISIBLE_CHAR;\r
+\r
+                       // Creates a caret container bogus element\r
+                       function createCaretContainer(fill) {\r
+                               var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});\r
+\r
+                               if (fill) {\r
+                                       caretContainer.appendChild(ed.getDoc().createTextNode(invisibleChar));\r
+                               }\r
+\r
+                               return caretContainer;\r
+                       };\r
+\r
+                       function isCaretContainerEmpty(node, nodes) {\r
+                               while (node) {\r
+                                       if ((node.nodeType === 3 && node.nodeValue !== invisibleChar) || node.childNodes.length > 1) {\r
+                                               return false;\r
+                                       }\r
 \r
-                       function hasPending() {\r
-                               return pendingFormats.apply.length || pendingFormats.remove.length;\r
+                                       // Collect nodes\r
+                                       if (nodes && node.nodeType === 1) {\r
+                                               nodes.push(node);\r
+                                       }\r
+\r
+                                       node = node.firstChild;\r
+                               }\r
+\r
+                               return true;\r
                        };\r
+                       \r
+                       // Returns any parent caret container element\r
+                       function getParentCaretContainer(node) {\r
+                               while (node) {\r
+                                       if (node.id === caretContainerId) {\r
+                                               return node;\r
+                                       }\r
 \r
-                       function resetPending() {\r
-                               pendingFormats.apply = [];\r
-                               pendingFormats.remove = [];\r
+                                       node = node.parentNode;\r
+                               }\r
                        };\r
 \r
-                       function perform(caret_node) {\r
-                               // Apply pending formats\r
-                               each(pendingFormats.apply.reverse(), function(item) {\r
-                                       apply(item.name, item.vars, caret_node);\r
+                       // Finds the first text node in the specified node\r
+                       function findFirstTextNode(node) {\r
+                               var walker;\r
 \r
-                                       // Colored nodes should be underlined so that the color of the underline matches the text color.\r
-                                       if (item.name === 'forecolor' && item.vars.value)\r
-                                               processUnderlineAndColor(caret_node.parentNode);\r
-                               });\r
+                               if (node) {\r
+                                       walker = new TreeWalker(node, node);\r
 \r
-                               // Remove pending formats\r
-                               each(pendingFormats.remove.reverse(), function(item) {\r
-                                       remove(item.name, item.vars, caret_node);\r
-                               });\r
+                                       for (node = walker.current(); node; node = walker.next()) {\r
+                                               if (node.nodeType === 3) {\r
+                                                       return node;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       };\r
+\r
+                       // Removes the caret container for the specified node or all on the current document\r
+                       function removeCaretContainer(node, move_caret) {\r
+                               var child, rng;\r
+\r
+                               if (!node) {\r
+                                       node = getParentCaretContainer(selection.getStart());\r
+\r
+                                       if (!node) {\r
+                                               while (node = dom.get(caretContainerId)) {\r
+                                                       removeCaretContainer(node, false);\r
+                                               }\r
+                                       }\r
+                               } else {\r
+                                       rng = selection.getRng(true);\r
+\r
+                                       if (isCaretContainerEmpty(node)) {\r
+                                               if (move_caret !== false) {\r
+                                                       rng.setStartBefore(node);\r
+                                                       rng.setEndBefore(node);\r
+                                               }\r
+\r
+                                               dom.remove(node);\r
+                                       } else {\r
+                                               child = findFirstTextNode(node);\r
+\r
+                                               if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {\r
+                                                       child = child.deleteData(0, 1);\r
+                                               }\r
+\r
+                                               dom.remove(node, 1);\r
+                                       }\r
+\r
+                                       selection.setRng(rng);\r
+                               }\r
+                       };\r
+                       \r
+                       // Applies formatting to the caret postion\r
+                       function applyCaretFormat() {\r
+                               var rng, caretContainer, textNode, offset, bookmark, container, text;\r
+\r
+                               rng = selection.getRng(true);\r
+                               offset = rng.startOffset;\r
+                               container = rng.startContainer;\r
+                               text = container.nodeValue;\r
 \r
-                               dom.remove(caret_node, 1);\r
-                               resetPending();\r
+                               caretContainer = getParentCaretContainer(selection.getStart());\r
+                               if (caretContainer) {\r
+                                       textNode = findFirstTextNode(caretContainer);\r
+                               }\r
+\r
+                               // Expand to word is caret is in the middle of a text node and the char before/after is a alpha numeric character\r
+                               if (text && offset > 0 && offset < text.length && /\w/.test(text.charAt(offset)) && /\w/.test(text.charAt(offset - 1))) {\r
+                                       // Get bookmark of caret position\r
+                                       bookmark = selection.getBookmark();\r
+\r
+                                       // Collapse bookmark range (WebKit)\r
+                                       rng.collapse(true);\r
+\r
+                                       // Expand the range to the closest word and split it at those points\r
+                                       rng = expandRng(rng, get(name));\r
+                                       rng = rangeUtils.split(rng);\r
+\r
+                                       // Apply the format to the range\r
+                                       apply(name, vars, rng);\r
+\r
+                                       // Move selection back to caret position\r
+                                       selection.moveToBookmark(bookmark);\r
+                               } else {\r
+                                       if (!caretContainer || textNode.nodeValue !== invisibleChar) {\r
+                                               caretContainer = createCaretContainer(true);\r
+                                               textNode = caretContainer.firstChild;\r
+\r
+                                               rng.insertNode(caretContainer);\r
+                                               offset = 1;\r
+\r
+                                               apply(name, vars, caretContainer);\r
+                                       } else {\r
+                                               apply(name, vars, caretContainer);\r
+                                       }\r
+\r
+                                       // Move selection to text node\r
+                                       selection.setCursorLocation(textNode, offset);\r
+                               }\r
                        };\r
 \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
+                       function removeCaretFormat() {\r
+                               var rng = selection.getRng(true), container, offset, bookmark,\r
+                                       hasContentAfter, node, formatNode, parents = [], i, caretContainer;\r
+\r
+                               container = rng.startContainer;\r
+                               offset = rng.startOffset;\r
+                               node = container;\r
+\r
+                               if (container.nodeType == 3) {\r
+                                       if (offset != container.nodeValue.length || container.nodeValue === invisibleChar) {\r
+                                               hasContentAfter = true;\r
+                                       }\r
+\r
+                                       node = node.parentNode;\r
+                               }\r
+\r
+                               while (node) {\r
+                                       if (matchNode(node, name, vars)) {\r
+                                               formatNode = node;\r
+                                               break;\r
+                                       }\r
+\r
+                                       if (node.nextSibling) {\r
+                                               hasContentAfter = true;\r
+                                       }\r
+\r
+                                       parents.push(node);\r
+                                       node = node.parentNode;\r
+                               }\r
+\r
+                               // Node doesn't have the specified format\r
+                               if (!formatNode) {\r
                                        return;\r
-                       }\r
+                               }\r
 \r
-                       currentPendingFormats.push({name : name, vars : vars});\r
+                               // Is there contents after the caret then remove the format on the element\r
+                               if (hasContentAfter) {\r
+                                       // Get bookmark of caret position\r
+                                       bookmark = selection.getBookmark();\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
+                                       // Collapse bookmark range (WebKit)\r
+                                       rng.collapse(true);\r
 \r
-                       // Pending apply or remove formats\r
-                       if (hasPending()) {\r
-                               ed.getDoc().execCommand('FontName', false, 'mceinline');\r
-                               pendingFormats.lastRng = selection.getRng();\r
+                                       // Expand the range to the closest word and split it at those points\r
+                                       rng = expandRng(rng, get(name), true);\r
+                                       rng = rangeUtils.split(rng);\r
 \r
-                               // IE will convert the current word\r
-                               each(dom.select('font,span'), function(node) {\r
-                                       var bookmark;\r
+                                       // Remove the format from the range\r
+                                       remove(name, vars, rng);\r
 \r
-                                       if (isCaretNode(node)) {\r
-                                               bookmark = selection.getBookmark();\r
-                                               perform(node);\r
-                                               selection.moveToBookmark(bookmark);\r
-                                               ed.nodeChanged();\r
+                                       // Move selection back to caret position\r
+                                       selection.moveToBookmark(bookmark);\r
+                               } else {\r
+                                       caretContainer = createCaretContainer();\r
+\r
+                                       node = caretContainer;\r
+                                       for (i = parents.length - 1; i >= 0; i--) {\r
+                                               node.appendChild(parents[i].cloneNode(false));\r
+                                               node = node.firstChild;\r
+                                       }\r
+\r
+                                       // Insert invisible character into inner most format element\r
+                                       node.appendChild(dom.doc.createTextNode(invisibleChar));\r
+                                       node = node.firstChild;\r
+\r
+                                       // Insert caret container after the formated node\r
+                                       dom.insertAfter(caretContainer, formatNode);\r
+\r
+                                       // Move selection to text node\r
+                                       selection.setCursorLocation(node, 1);\r
+                               }\r
+                       };\r
+\r
+                       // Only bind the caret events once\r
+                       if (!self._hasCaretEvents) {\r
+                               // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements\r
+                               ed.onBeforeGetContent.addToTop(function() {\r
+                                       var nodes = [], i;\r
+\r
+                                       if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {\r
+                                               // Mark children\r
+                                               i = nodes.length;\r
+                                               while (i--) {\r
+                                                       dom.setAttrib(nodes[i], 'data-mce-bogus', '1');\r
+                                               }\r
                                        }\r
                                });\r
 \r
-                               // Only register listeners once if we need to\r
-                               if (!pendingFormats.isListening && hasPending()) {\r
-                                       pendingFormats.isListening = true;\r
-                                       function performPendingFormat(node, textNode) {\r
-                                               var rng = dom.createRng();\r
-                                               perform(node);\r
+                               // Remove caret container on mouse up and on key up\r
+                               tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {\r
+                                       ed[name].addToTop(function() {\r
+                                               removeCaretContainer();\r
+                                       });\r
+                               });\r
 \r
-                                               rng.setStart(textNode, textNode.nodeValue.length);\r
-                                               rng.setEnd(textNode, textNode.nodeValue.length);\r
-                                               selection.setRng(rng);\r
-                                               ed.nodeChanged();\r
+                               // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys\r
+                               ed.onKeyDown.addToTop(function(ed, e) {\r
+                                       var keyCode = e.keyCode;\r
+\r
+                                       if (keyCode == 8 || keyCode == 37 || keyCode == 39) {\r
+                                               removeCaretContainer(getParentCaretContainer(selection.getStart()));\r
                                        }\r
-                                       var enterKeyPressed = false;\r
+                               });\r
 \r
-                                       each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {\r
-                                               ed[event].addToTop(function(ed, e) {\r
-                                                       if (e.keyCode==13 && !e.shiftKey) {\r
-                                                               enterKeyPressed = true;\r
-                                                               return;\r
-                                                       }\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
-                                                               var foundCaret = false;\r
-                                                               each(dom.select('font,span'), function(node) {\r
-                                                                       var textNode, rng;\r
-\r
-                                                                       // Look for marker\r
-                                                                       if (isCaretNode(node)) {\r
-                                                                               foundCaret = true;\r
-                                                                               textNode = node.firstChild;\r
-\r
-                                                                               // Find the first text node within node\r
-                                                                               while (textNode && textNode.nodeType != 3)\r
-                                                                                       textNode = textNode.firstChild;\r
-\r
-                                                                               if (textNode) \r
-                                                                                       performPendingFormat(node, textNode);\r
-                                                                               else\r
-                                                                                       dom.remove(node);\r
-                                                                       }\r
-                                                               });\r
-                                                               \r
-                                                               // no caret - so we are \r
-                                                               if (enterKeyPressed && !foundCaret) {\r
-                                                                       var node = selection.getNode();\r
-                                                                       var textNode = node;\r
-\r
-                                                                       // Find the first text node within node\r
-                                                                       while (textNode && textNode.nodeType != 3)\r
-                                                                               textNode = textNode.firstChild;\r
-                                                                       if (textNode) {\r
-                                                                               node=textNode.parentNode;\r
-                                                                               while (!isBlock(node)){\r
-                                                                                       node=node.parentNode;\r
-                                                                               }\r
-                                                                               performPendingFormat(node, textNode);\r
-                                                                       }\r
-                                                               }\r
+                               self._hasCaretEvents = true;\r
+                       }\r
 \r
-                                                               // Always unbind and clear pending styles on keyup\r
-                                                               if (e.type == 'keyup' || e.type == 'mouseup') {\r
-                                                                       resetPending();\r
-                                                                       enterKeyPressed=false;\r
-                                                               }\r
-                                                       }\r
-                                               });\r
-                                       });\r
+                       // Do apply or remove caret format\r
+                       if (type == "apply") {\r
+                               applyCaretFormat();\r
+                       } else {\r
+                               removeCaretFormat();\r
+                       }\r
+               };\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
        };\r
 })(tinymce);\r
 \r
@@ -16385,7 +16789,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
        var filters, fontSizes, dom, settings = ed.settings;\r
 \r
        if (settings.inline_styles) {\r
-               fontSizes = tinymce.explode(settings.font_size_style_values);\r
+               fontSizes = tinymce.explode(settings.font_size_legacy_values);\r
 \r
                function replaceWithSpan(node, styles) {\r
                        tinymce.each(styles, function(value, name) {\r