Aleksander Machniak
2012-07-24 0afe27901189a5416dc696eef32e2bc2e5fe3844
program/js/tiny_mce/tiny_mce_src.js
@@ -6,9 +6,9 @@
   var tinymce = {
      majorVersion : '3',
      minorVersion : '5',
      minorVersion : '5.4.1',
      releaseDate : '2012-05-03',
      releaseDate : '2012-06-24',
      _init : function() {
         var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -880,12 +880,12 @@
            ((s) ? "; secure" : "");
      },
      remove : function(n, p) {
         var d = new Date();
      remove : function(name, path, domain) {
         var date = new Date();
         d.setTime(d.getTime() - 1000);
         date.setTime(date.getTime() - 1000);
         this.set(n, '', d, p, d);
         this.set(name, '', date, path, domain);
      }
   });
})();
@@ -1083,6 +1083,10 @@
      modifierPressed: function (e) {
         return e.shiftKey || e.ctrlKey || e.altKey;
      },
      metaKeyPressed: function(e) {
         return tinymce.isMac ? e.metaKey : e.ctrlKey;
      }
   };
})(tinymce);
@@ -1097,6 +1101,12 @@
         // Ignore
      }
   }
   function getDocumentMode() {
      var documentMode = editor.getDoc().documentMode;
      return documentMode ? documentMode : 6;
   };
   function cleanupStylesWhenDeleting() {
      function removeMergedFormatSpans(isDelete) {
@@ -1157,74 +1167,58 @@
   };
   
   function emptyEditorWhenDeleting() {
      function getEndPointNode(rng, start) {
         var container, offset, prefix = start ? 'start' : 'end';
      function serializeRng(rng) {
         var body = dom.create("body");
         var contents = rng.cloneContents();
         body.appendChild(contents);
         return selection.serializer.serialize(body, {format: 'html'});
      }
         container = rng[prefix + 'Container'];
         offset = rng[prefix + 'Offset'];
      function allContentsSelected(rng) {
         var selection = serializeRng(rng);
         // Resolve indexed container
         if (container.nodeType == 1 && container.hasChildNodes()) {
            container = container.childNodes[Math.min(start ? offset : (offset > 0 ? offset - 1 : 0), container.childNodes.length - 1)]
         }
         var allRng = dom.createRng();
         allRng.selectNode(editor.getBody());
         return container;
      };
         var allSelection = serializeRng(allRng);//console.log(selection, "----", allSelection);
         return selection === allSelection;
      }
      function isAtStartEndOfBody(rng, start) {
         var container, offset, root, childNode, prefix = start ? 'start' : 'end', isAfter;
      editor.onKeyDown.add(function(editor, e) {
         var keyCode = e.keyCode, isCollapsed;
         container = rng[prefix + 'Container'];
         offset = rng[prefix + 'Offset'];
         root = dom.getRoot();
         // Resolve indexed container
         if (container.nodeType == 1) {
            isAfter = offset >= container.childNodes.length;
            container = getEndPointNode(rng, start);
            if (container.nodeType == 3) {
               offset = start && !isAfter ? 0 : container.nodeValue.length;
            }
         }
         // Check if start/end is in the middle of text
         if (container.nodeType == 3 && ((start && offset > 0) || (!start && offset < container.nodeValue.length))) {
            return false;
         }
         // Walk up the DOM tree to see if the endpoint is at the beginning/end of body
         while (container !== root) {
            childNode = container.parentNode[start ? 'firstChild' : 'lastChild'];
            // If first/last element is a BR then jump to it's sibling in case: <p>x<br></p>
            if (childNode.nodeName == "BR") {
               childNode = childNode[start ? 'nextSibling' : 'previousSibling'] || childNode;
            }
            // If the childNode isn't the container node then break in case <p><span>A</span>[X]</p>
            if (childNode !== container) {
               return false;
            }
            container = container.parentNode;
         }
         return true;
      };
      editor.onKeyDown.addToTop(function(editor, e) {
         var rng, keyCode = e.keyCode;
         // Empty the editor if it's needed for example backspace at <p><b>|</b></p>
         if (!e.isDefaultPrevented() && (keyCode == DELETE || keyCode == BACKSPACE)) {
            rng = selection.getRng(true);
            isCollapsed = editor.selection.isCollapsed();
            if (isAtStartEndOfBody(rng, true) && isAtStartEndOfBody(rng, false) &&
               (rng.collapsed || dom.findCommonAncestor(getEndPointNode(rng, true), getEndPointNode(rng)) === dom.getRoot())) {
               editor.setContent('');
               editor.nodeChanged();
               e.preventDefault();
            // Selection is collapsed but the editor isn't empty
            if (isCollapsed && !dom.isEmpty(editor.getBody())) {
               return;
            }
            // IE deletes all contents correctly when everything is selected
            if (tinymce.isIE && !isCollapsed) {
               return;
            }
            // Selection isn't collapsed but not all the contents is selected
            if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
               return;
            }
            // Manually empty the editor
            editor.setContent('');
            editor.selection.setCursorLocation(editor.getBody(), 0);
            editor.nodeChanged();
         }
      });
   };
   function selectAll() {
      editor.onKeyDown.add(function(editor, e) {
         if (e.keyCode == 65 && VK.metaKeyPressed(e)) {
            e.preventDefault();
            editor.execCommand('SelectAll');
         }
      });
   };
@@ -1393,16 +1387,15 @@
   }
   function addNewLinesBeforeBrInPre() {
      var documentMode = editor.getDoc().documentMode;
      // IE8+ rendering mode does the right thing with BR in PRE
      if (documentMode && documentMode > 7) {
      if (getDocumentMode() > 7) {
         return;
      }
       // Enable display: none in area and add a specific class that hides all BR elements in PRE to
       // avoid the caret from getting stuck at the BR elements while pressing the right arrow key
      setEditorCommandState('RespectVisibilityInDesign', true);
      editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
      dom.addClass(editor.getBody(), 'mceHideBrInPre');
      // Adds a \n before all BR elements in PRE to get them visual
@@ -1588,6 +1581,14 @@
      editor.onSetContent.add(selection.onSetContent.add(fixLinks));
   };
   function setDefaultBlockType() {
      if (settings.forced_root_block) {
         editor.onInit.add(function() {
            setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
         });
      }
   }
   function removeGhostSelection() {
      function repaint(sender, args) {
         if (!sender || !args.initial) {
@@ -1600,13 +1601,105 @@
      editor.onSetContent.add(repaint);
   };
   function deleteImageOnBackSpace() {
   function deleteControlItemOnBackSpace() {
      editor.onKeyDown.add(function(editor, e) {
         if (!e.isDefaultPrevented() && e.keyCode == 8 && selection.getNode().nodeName == 'IMG') {
         var rng;
         if (!e.isDefaultPrevented() && e.keyCode == BACKSPACE) {
            rng = editor.getDoc().selection.createRange();
            if (rng && rng.item) {
               e.preventDefault();
               editor.undoManager.beforeChange();
               dom.remove(rng.item(0));
               editor.undoManager.add();
            }
         }
      });
   };
   function renderEmptyBlocksFix() {
      var emptyBlocksCSS;
      // IE10+
      if (getDocumentMode() >= 10) {
         emptyBlocksCSS = '';
         tinymce.each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
            emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
         });
         editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
      }
   };
   function fakeImageResize() {
      var mouseDownImg, startX, startY, startW, startH;
      if (!settings.object_resizing || settings.webkit_fake_resize === false) {
         return;
      }
      editor.contentStyles.push('.mceResizeImages img {cursor: se-resize !important}');
      function resizeImage(e) {
         var deltaX, deltaY, ratio, width, height;
         if (mouseDownImg) {
            deltaX = e.screenX - startX;
            deltaY = e.screenY - startY;
            ratio = Math.max((startW + deltaX) / startW, (startH + deltaY) / startH);
            // Only update styles if the user draged one pixel or more
            if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) {
               // Constrain proportions
               width = Math.round(startW * ratio);
               height = Math.round(startH * ratio);
               // Resize by using style or attribute
               if (mouseDownImg.style.width) {
                  dom.setStyle(mouseDownImg, 'width', width);
               } else {
                  dom.setAttrib(mouseDownImg, 'width', width);
               }
               // Resize by using style or attribute
               if (mouseDownImg.style.height) {
                  dom.setStyle(mouseDownImg, 'height', height);
               } else {
                  dom.setAttrib(mouseDownImg, 'height', height);
               }
               if (!dom.hasClass(editor.getBody(), 'mceResizeImages')) {
                  dom.addClass(editor.getBody(), 'mceResizeImages');
               }
            }
         }
      };
      editor.onMouseDown.add(function(editor, e) {
         var target = e.target;
         if (target.nodeName == "IMG") {
            mouseDownImg = target;
            startX = e.screenX;
            startY = e.screenY;
            startW = mouseDownImg.clientWidth;
            startH = mouseDownImg.clientHeight;
            dom.bind(editor.getDoc(), 'mousemove', resizeImage);
            e.preventDefault();
            editor.undoManager.beforeChange();
            dom.remove(selection.getNode());
            editor.undoManager.add();
         }
      });
      // Unbind events on node change and restore resize cursor
      editor.onNodeChange.add(function() {
         if (mouseDownImg) {
            mouseDownImg = null;
            dom.unbind(editor.getDoc(), 'mousemove', resizeImage);
         }
         if (selection.getNode().nodeName == "IMG") {
            dom.addClass(editor.getBody(), 'mceResizeImages');
         } else {
            dom.removeClass(editor.getBody(), 'mceResizeImages');
         }
      });
   };
@@ -1622,10 +1715,14 @@
      cleanupStylesWhenDeleting();
      inputMethodFocus();
      selectControlElements();
      setDefaultBlockType();
      // iOS
      if (tinymce.isIDevice) {
         selectionChangeNodeChanged();
      } else {
         fakeImageResize();
         selectAll();
      }
   }
@@ -1635,7 +1732,8 @@
      ensureBodyHasRoleApplication();
      addNewLinesBeforeBrInPre();
      removePreSerializedStylesWhenSelectingControls();
      deleteImageOnBackSpace();
      deleteControlItemOnBackSpace();
      renderEmptyBlocksFix();
   }
   // Gecko
@@ -2098,9 +2196,12 @@
      if (!html5) {
         html5 = mapCache.html5 = unpack({
               A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title',
               B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video',
               C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
               A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
               B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|' +
                  'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr',
               C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|' +
                  'figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|' +
                  'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
            }, 'html[A|manifest][body|head]' +
               'head[A][base|command|link|meta|noscript|script|style|title]' +
               'title[A][#]' +
@@ -2136,7 +2237,7 @@
               'dl[A][dd|dt]' +
               'dt[A][B]' +
               'dd[A][C]' +
               'a[A|href|target|ping|rel|media|type][C]' +
               'a[A|href|target|ping|rel|media|type][B]' +
               'em[A][B]' +
               'strong[A][B]' +
               'small[A][B]' +
@@ -2182,7 +2283,8 @@
               'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +
               'fieldset[A|disabled|form|name][C|legend]' +
               'label[A|form|for][B]' +
               'input[A|type|accept|alt|autocomplete|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value][]' +
               'input[A|type|accept|alt|autocomplete|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|' +
                  'multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]' +
               'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +
               'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +
               'datalist[A][B|option]' +
@@ -2196,7 +2298,7 @@
               'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +
               'mathml[A][]' +
               'svg[A][]' +
               'table[A|summary][caption|colgroup|thead|tfoot|tbody|tr]' +
               'table[A|border][caption|colgroup|thead|tfoot|tbody|tr]' +
               'caption[A][C]' +
               'colgroup[A|span][col]' +
               'col[A|span][]' +
@@ -2205,7 +2307,8 @@
               'tbody[A][tr]' +
               'tr[A][th|td]' +
               'th[A|headers|rowspan|colspan|scope][B]' +
               'td[A|headers|rowspan|colspan][C]'
               'td[A|headers|rowspan|colspan][C]' +
               'wbr[A][]'
         );
      }
@@ -2385,13 +2488,13 @@
      // Setup map objects
      whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script style textarea');
      selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li options p td tfoot th thead tr');
      shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source');
      selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
      shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link meta param embed source wbr');
      boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls');
      nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object', shortEndedElementsMap);
      blockElementsMap = createLookupTable('block_elements', 'h1 h2 h3 h4 h5 h6 hr p div address pre form table tbody thead tfoot ' + 
                  'th tr td li ol ul caption blockquote center dl dt dd dir fieldset ' + 
                  'noscript menu isindex samp header footer article section hgroup aside nav figure');
                  'noscript menu isindex samp header footer article section hgroup aside nav figure option datalist select optgroup');
      // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
      function patternToRegExp(str) {
@@ -2713,6 +2816,36 @@
         return !!(parent && parent[child]);
      };
      self.isValid = function(name, attr) {
         var attrPatterns, i, rule = getElementRule(name);
         // Check if it's a valid element
         if (rule) {
            if (attr) {
               // Check if attribute name exists
               if (rule.attributes[attr]) {
                  return true;
               }
               // Check if attribute matches a regexp pattern
               attrPatterns = rule.attributePatterns;
               if (attrPatterns) {
                  i = attrPatterns.length;
                  while (i--) {
                     if (attrPatterns[i].pattern.test(name)) {
                        return true;
                     }
                  }
               }
            } else {
               return true;
            }
         }
         // No match
         return false;
      };
      self.getElementRule = getElementRule;
      self.getCustomElements = function() {
@@ -2836,7 +2969,7 @@
         // Setup lookup tables for empty elements and boolean attributes
         shortEndedElements = schema.getShortEndedElements();
         selfClosing = schema.getSelfClosingElements();
         selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
         fillAttrsMap = schema.getBoolAttrs();
         validate = settings.validate;
         removeInternalElements = settings.remove_internals;
@@ -3584,9 +3717,23 @@
            }
         };
         function cloneAndExcludeBlocks(input) {
            var name, output = {};
            for (name in input) {
               if (name !== 'li' && name != 'p') {
                  output[name] = input[name];
               }
            }
            return output;
         };
         parser = new tinymce.html.SaxParser({
            validate : validate,
            fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results
            // Exclude P and LI from DOM parsing since it's treated better by the DOM parser
            self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
            cdata: function(text) {
               node.append(createNode('#cdata', 4)).value = text;
@@ -3764,7 +3911,7 @@
                           node.empty().append(new Node('#text', '3')).value = '\u00a0';
                        else {
                           // Leave nodes that have a name like <a name="name">
                           if (!node.attributes.map.name) {
                           if (!node.attributes.map.name && !node.attributes.map.id) {
                              tempNode = node.parent;
                              node.empty().remove();
                              node = tempNode;
@@ -3916,12 +4063,12 @@
      // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
      if (!settings.allow_html_in_named_anchor) {
         self.addAttributeFilter('name', function(nodes, name) {
         self.addAttributeFilter('id,name', function(nodes, name) {
            var i = nodes.length, sibling, prevSibling, parent, node;
            while (i--) {
               node = nodes[i];
               if (node.name === 'a' && node.firstChild) {
               if (node.name === 'a' && node.firstChild && !node.attr('href')) {
                  parent = node.parent;
                  // Move children after current node
@@ -4600,7 +4747,7 @@
         // Old API supported multiple targets
         if (target && target instanceof Array) {
            var i = target;
            var i = target.length;
            while (i--) {
               self.add(target[i], events, func, scope);
@@ -4660,12 +4807,20 @@
      };
      self.prevent = function(e) {
         if (!e.preventDefault) {
            e = fix(e);
         }
         e.preventDefault();
         return false;
      };
      self.stop = function(e) {
         if (!e.stopPropagation) {
            e = fix(e);
         }
         e.stopPropagation();
         return false;
@@ -5434,6 +5589,32 @@
         return this.styles.serialize(o, name);
      },
      addStyle: function(cssText) {
         var doc = this.doc, head;
         // Create style element if needed
         styleElm = doc.getElementById('mceDefaultStyles');
         if (!styleElm) {
            styleElm = doc.createElement('style'),
            styleElm.id = 'mceDefaultStyles';
            styleElm.type = 'text/css';
            head = doc.getElementsByTagName('head')[0]
            if (head.firstChild) {
               head.insertBefore(styleElm, head.firstChild);
            } else {
               head.appendChild(styleElm);
            }
         }
         // Append style data to old or new style element
         if (styleElm.styleSheet) {
            styleElm.styleSheet.cssText += cssText;
         } else {
            styleElm.appendChild(doc.createTextNode(cssText));
         }
      },
      loadCSS : function(u) {
         var t = this, d = t.doc, head;
@@ -5557,13 +5738,13 @@
                  // This seems to fix this problem
                  // Create new div with HTML contents and a BR infront to keep comments
                  element = self.create('div');
                  element.innerHTML = '<br />' + html;
                  var newElement = self.create('div');
                  newElement.innerHTML = '<br />' + html;
                  // Add all children from div to target
                  each (element.childNodes, function(node, i) {
                  each (tinymce.grep(newElement.childNodes), function(node, i) {
                     // Skip br element
                     if (i)
                     if (i && element.canHaveHTML)
                        element.appendChild(node);
                  });
               }
@@ -6158,7 +6339,8 @@
         cloneContents : cloneContents,
         insertNode : insertNode,
         surroundContents : surroundContents,
         cloneRange : cloneRange
         cloneRange : cloneRange,
         toStringIE : toStringIE
      });
      function createDocumentFragment() {
@@ -6798,9 +6980,20 @@
         n.parentNode.removeChild(n);
      };
      function toStringIE() {
         return dom.create('body', null, cloneContents()).outerText;
      }
      return t;
   };
   ns.Range = Range;
   // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
   Range.prototype.toString = function() {
      return this.toStringIE();
   };
})(tinymce.dom);
(function() {
@@ -7156,7 +7349,7 @@
      };
      this.addRange = function(rng) {
         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;
         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling, doc = selection.dom.doc, body = doc.body;
         function setEndPoint(start) {
            var container, offset, marker, tmpRng, nodes;
@@ -7214,11 +7407,25 @@
            // Trick to place the caret inside an empty block element like <p></p>
            if (startOffset == endOffset && !startContainer.hasChildNodes()) {
               if (startContainer.canHaveHTML) {
                  // Check if previous sibling is an empty block if it is then we need to render it
                  // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
                  // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
                  sibling = startContainer.previousSibling;
                  if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
                     sibling.innerHTML = '\uFEFF';
                  } else {
                     sibling = null;
                  }
                  startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';
                  ieRng.moveToElementText(startContainer.lastChild);
                  ieRng.select();
                  dom.doc.selection.clear();
                  startContainer.innerHTML = '';
                  if (sibling) {
                     sibling.innerHTML = '';
                  }
                  return;
               } else {
                  startOffset = dom.nodeIndex(startContainer);
@@ -8816,12 +9023,13 @@
   var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;
   tinymce.create('tinymce.dom.Selection', {
      Selection : function(dom, win, serializer) {
      Selection : function(dom, win, serializer, editor) {
         var t = this;
         t.dom = dom;
         t.win = win;
         t.serializer = serializer;
         t.editor = editor;
         // Add events
         each([
@@ -8977,7 +9185,7 @@
      },
      getStart : function() {
         var rng = this.getRng(), startElement, parentElement, checkRng, node;
         var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
         if (rng.duplicate || rng.item) {
            // Control selection, return first item
@@ -8988,6 +9196,9 @@
            checkRng = rng.duplicate();
            checkRng.collapse(1);
            startElement = checkRng.parentElement();
            if (startElement.ownerDocument !== self.dom.doc) {
               startElement = self.dom.getRoot();
            }
            // Check if range parent is inside the start element, then return the inner parent element
            // This will fix issues when a single element is selected, IE would otherwise return the wrong start element
@@ -9014,31 +9225,34 @@
      },
      getEnd : function() {
         var t = this, r = t.getRng(), e, eo;
         var self = this, rng = self.getRng(), endElement, endOffset;
         if (r.duplicate || r.item) {
            if (r.item)
               return r.item(0);
         if (rng.duplicate || rng.item) {
            if (rng.item)
               return rng.item(0);
            r = r.duplicate();
            r.collapse(0);
            e = r.parentElement();
            rng = rng.duplicate();
            rng.collapse(0);
            endElement = rng.parentElement();
            if (endElement.ownerDocument !== self.dom.doc) {
               endElement = self.dom.getRoot();
            }
            if (e && e.nodeName == 'BODY')
               return e.lastChild || e;
            if (endElement && endElement.nodeName == 'BODY')
               return endElement.lastChild || endElement;
            return e;
            return endElement;
         } else {
            e = r.endContainer;
            eo = r.endOffset;
            endElement = rng.endContainer;
            endOffset = rng.endOffset;
            if (e.nodeType == 1 && e.hasChildNodes())
               e = e.childNodes[eo > 0 ? eo - 1 : eo];
            if (endElement.nodeType == 1 && endElement.hasChildNodes())
               endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
            if (e && e.nodeType == 3)
               return e.parentNode;
            if (endElement && endElement.nodeType == 3)
               return endElement.parentNode;
            return e;
            return endElement;
         }
      },
@@ -9784,14 +9998,66 @@
         }
      },
      destroy : function(s) {
         var t = this;
      selectorChanged: function(selector, callback) {
         var self = this, currentSelectors;
         t.win = null;
         if (!self.selectorChangedData) {
            self.selectorChangedData = {};
            currentSelectors = {};
            self.editor.onNodeChange.addToTop(function(ed, cm, node) {
               var dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
               // Check for new matching selectors
               each(self.selectorChangedData, function(callbacks, selector) {
                  each(parents, function(node) {
                     if (dom.is(node, selector)) {
                        if (!currentSelectors[selector]) {
                           // Execute callbacks
                           each(callbacks, function(callback) {
                              callback(true, {node: node, selector: selector, parents: parents});
                           });
                           currentSelectors[selector] = callbacks;
                        }
                        matchedSelectors[selector] = callbacks;
                        return false;
                     }
                  });
               });
               // Check if current selectors still match
               each(currentSelectors, function(callbacks, selector) {
                  if (!matchedSelectors[selector]) {
                     delete currentSelectors[selector];
                     each(callbacks, function(callback) {
                        callback(false, {node: node, selector: selector, parents: parents});
                     });
                  }
               });
            });
         }
         // Add selector listeners
         if (!self.selectorChangedData[selector]) {
            self.selectorChangedData[selector] = [];
         }
         self.selectorChangedData[selector].push(callback);
         return self;
      },
      destroy : function(manual) {
         var self = this;
         self.win = null;
         // Manual destroy then remove unload handler
         if (!s)
            tinymce.removeUnload(t.destroy);
         if (!manual)
            tinymce.removeUnload(self.destroy);
      },
      // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode
@@ -10207,11 +10473,10 @@
         }
         // Create new script element
         elm = dom.create('script', {
            id : id,
            type : 'text/javascript',
            src : tinymce._addVer(url)
         });
         elm = document.createElement('script');
         elm.id = id;
         elm.type = 'text/javascript';
         elm.src = tinymce._addVer(url);
         // Add onload listener for non IE browsers since IE9
         // fires onload event before the script is parsed and executed
@@ -10575,12 +10840,15 @@
         t.destroy = function() {
            each(items, function(item) {
               dom.unbind(dom.get(item.id), 'focus', itemFocussed);
               dom.unbind(dom.get(item.id), 'blur', itemBlurred);
               var elm = dom.get(item.id);
               dom.unbind(elm, 'focus', itemFocussed);
               dom.unbind(elm, 'blur', itemBlurred);
            });
            dom.unbind(dom.get(root), 'focus', rootFocussed);
            dom.unbind(dom.get(root), 'keydown', rootKeydown);
            var rootElm = dom.get(root);
            dom.unbind(rootElm, 'focus', rootFocussed);
            dom.unbind(rootElm, 'keydown', rootKeydown);
            items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;
            t.destroy = function() {};
@@ -10659,21 +10927,23 @@
         // Set up state and listeners for each item.
         each(items, function(item, idx) {
            var tabindex;
            var tabindex, elm;
            if (!item.id) {
               item.id = dom.uniqueId('_mce_item_');
            }
            elm = dom.get(item.id);
            if (excludeFromTabOrder) {
               dom.bind(item.id, 'blur', itemBlurred);
               dom.bind(elm, 'blur', itemBlurred);
               tabindex = '-1';
            } else {
               tabindex = (idx === 0 ? '0' : '-1');
            }
            dom.setAttrib(item.id, 'tabindex', tabindex);
            dom.bind(dom.get(item.id), 'focus', itemFocussed);
            elm.setAttribute('tabindex', tabindex);
            dom.bind(elm, 'focus', itemFocussed);
         });
         
         // Setup initial state for root element.
@@ -10682,10 +10952,11 @@
         }
         dom.setAttrib(root, 'tabindex', '-1');
         // Setup listeners for root element.
         dom.bind(dom.get(root), 'focus', rootFocussed);
         dom.bind(dom.get(root), 'keydown', rootKeydown);
         var rootElm = dom.get(root);
         dom.bind(rootElm, 'focus', rootFocussed);
         dom.bind(rootElm, 'keydown', rootKeydown);
      }
   });
})(tinymce);
@@ -11326,7 +11597,7 @@
         l = DOM.encode(s.label || '');
         h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';
         if (s.image && !(this.editor  &&this.editor.forcedHighContrastMode) )
            h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;
            h += '<span class="mceIcon ' + s['class'] + '"><img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" /></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
         else
            h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
@@ -12034,6 +12305,16 @@
            DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
         }
         t.keyboardNav = new tinymce.ui.KeyboardNavigation({
            root: t.id + '_menu',
            items: DOM.select('a', t.id + '_menu'),
            onCancel: function() {
               t.hideMenu();
               t.focus();
            }
         });
         t.keyboardNav.focus();
         t.isMenuVisible = 1;
      },
@@ -12054,6 +12335,7 @@
            t.isMenuVisible = 0;
            t.onHideMenu.dispatch();
            t.keyboardNav.destroy();
         }
      },
@@ -12118,15 +12400,6 @@
         }
         DOM.addClass(m, 'mceColorSplitMenu');
         new tinymce.ui.KeyboardNavigation({
            root: t.id + '_menu',
            items: DOM.select('a', t.id + '_menu'),
            onCancel: function() {
               t.hideMenu();
               t.focus();
            }
         });
         // Prevent IE from scrolling and hindering click to occur #4019
         Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});
@@ -12168,11 +12441,17 @@
      },
      destroy : function() {
         this.parent();
         var self = this;
         Event.clear(this.id + '_menu');
         Event.clear(this.id + '_more');
         DOM.remove(this.id + '_menu');
         self.parent();
         Event.clear(self.id + '_menu');
         Event.clear(self.id + '_more');
         DOM.remove(self.id + '_menu');
         if (self.keyboardNav) {
            self.keyboardNav.destroy();
         }
      }
   });
})(tinymce);
@@ -12483,11 +12762,6 @@
            return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
         };
         s = extend({
            theme : "simple",
            language : "en"
         }, s);
         t.settings = s;
         // Legacy call
@@ -12767,7 +13041,7 @@
         self.settings = settings = extend({
            id : id,
            language : 'en',
            theme : 'simple',
            theme : 'advanced',
            skin : 'default',
            delta_width : 0,
            delta_height : 0,
@@ -12798,8 +13072,8 @@
            inline_styles : TRUE,
            convert_fonts_to_spans : TRUE,
            indent : 'simple',
            indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure',
            indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure',
            indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
            indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
            validate : TRUE,
            entity_encoding : 'named',
            url_converter : self.convertURL,
@@ -12820,6 +13094,8 @@
         self.baseURI = tinymce.baseURI;
         self.contentCSS = [];
         self.contentStyles = [];
         // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
         self.setupEvents();
@@ -12858,6 +13134,12 @@
         // Add hidden input for non input elements inside form elements
         if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
            DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
         // Hide target element early to prevent content flashing
         if (!s.content_editable) {
            t.orgVisibility = t.getElement().style.visibility;
            t.getElement().style.visibility = 'hidden';
         }
         if (tinymce.WindowManager)
            t.windowManager = new tinymce.WindowManager(t);
@@ -12920,7 +13202,7 @@
            if (s.language && s.language_load !== false)
               sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
            if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
            if (s.theme && typeof s.theme != "function" && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
               ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
            each(explode(s.plugins), function(p) {
@@ -12954,20 +13236,25 @@
      },
      init : function() {
         var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
         var n, t = this, s = t.settings, w, h, mh, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
         tinymce.add(t);
         s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
         if (s.theme) {
            s.theme = s.theme.replace(/-/, '');
            o = ThemeManager.get(s.theme);
            t.theme = new o();
            if (typeof s.theme != "function") {
               s.theme = s.theme.replace(/-/, '');
               o = ThemeManager.get(s.theme);
               t.theme = new o();
            if (t.theme.init)
               t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
               if (t.theme.init)
                  t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
            } else {
               t.theme = s.theme;
            }
         }
         function initPlugin(p) {
            var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
            if (c && tinymce.inArray(initializedPlugins,p) === -1) {
@@ -13001,36 +13288,68 @@
         t.controlManager = new tinymce.ControlManager(t);
         t.onExecCommand.add(function(ed, c) {
            // Don't refresh the select lists until caret move
            if (!/^(FontName|FontSize)$/.test(c))
               t.nodeChanged();
         });
         // Enables users to override the control factory
         t.onBeforeRenderUI.dispatch(t, t.controlManager);
         // Measure box
         if (s.render_ui && t.theme) {
            w = s.width || e.style.width || e.offsetWidth;
            h = s.height || e.style.height || e.offsetHeight;
            t.orgDisplay = e.style.display;
            re = /^[0-9\.]+(|px)$/i;
            if (re.test('' + w))
               w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
            if (typeof s.theme != "function") {
               w = s.width || e.style.width || e.offsetWidth;
               h = s.height || e.style.height || e.offsetHeight;
               mh = s.min_height || 100;
               re = /^[0-9\.]+(|px)$/i;
            if (re.test('' + h))
               h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), 100);
               if (re.test('' + w))
                  w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
            // Render UI
            o = t.theme.renderUI({
               targetNode : e,
               width : w,
               height : h,
               deltaWidth : s.delta_width,
               deltaHeight : s.delta_height
            });
               if (re.test('' + h))
                  h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), mh);
               // Render UI
               o = t.theme.renderUI({
                  targetNode : e,
                  width : w,
                  height : h,
                  deltaWidth : s.delta_width,
                  deltaHeight : s.delta_height
               });
               // Resize editor
               DOM.setStyles(o.sizeContainer || o.editorContainer, {
                  width : w,
                  height : h
               });
               h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
               if (h < mh)
                  h = mh;
            } else {
               o = s.theme(t, e);
               // Convert element type to id:s
               if (o.editorContainer.nodeType) {
                  o.editorContainer = o.editorContainer.id = o.editorContainer.id || t.id + "_parent";
               }
               // Convert element type to id:s
               if (o.iframeContainer.nodeType) {
                  o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || t.id + "_iframecontainer";
               }
               // Use specified iframe height or the targets offsetHeight
               h = o.iframeHeight || e.offsetHeight;
               // Store away the selection when it's changed to it can be restored later with a editor.focus() call
               if (isIE) {
                  t.onInit.add(function(ed) {
                     ed.dom.bind(ed.getBody(), 'beforedeactivate keydown', function() {
                        ed.lastIERng = ed.selection.getRng();
                     });
                  });
               }
            }
            t.editorContainer = o.editorContainer;
         }
@@ -13051,16 +13370,6 @@
         // User specified a document.domain value
         if (document.domain && location.hostname != document.domain)
            tinymce.relaxedDomain = document.domain;
         // Resize editor
         DOM.setStyles(o.sizeContainer || o.editorContainer, {
            width : w,
            height : h
         });
         h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
         if (h < 100)
            h = 100;
         t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
@@ -13120,7 +13429,14 @@
         });
         t.contentAreaContainer = o.iframeContainer;
         DOM.get(o.editorContainer).style.display = t.orgDisplay;
         if (o.editorContainer) {
            DOM.get(o.editorContainer).style.display = t.orgDisplay;
         }
         // Restore visibility on target element
         e.style.visibility = t.orgVisibility;
         DOM.get(t.id).style.display = 'none';
         DOM.setAttrib(t.id, 'aria-hidden', true);
@@ -13131,7 +13447,7 @@
      },
      initContentBody : function() {
         var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body;
         var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body, contentCssText;
         // Setup iframe body
         if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {
@@ -13230,7 +13546,7 @@
         self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);
         self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer);
         self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer, self);
         self.formatter = new tinymce.Formatter(self);
@@ -13239,6 +13555,12 @@
         self.forceBlocks = new tinymce.ForceBlocks(self);
         self.enterKey = new tinymce.EnterKey(self);
         self.editorCommands = new tinymce.EditorCommands(self);
         self.onExecCommand.add(function(editor, command) {
            // Don't refresh the select lists until caret move
            if (!/^(FontName|FontSize)$/.test(command))
               self.nodeChanged();
         });
         // Pass through
         self.serializer.onPreProcess.add(function(se, o) {
@@ -13251,7 +13573,7 @@
         self.onPreInit.dispatch(self);
         if (!settings.gecko_spellcheck)
         if (!settings.browser_spellcheck && !settings.gecko_spellcheck)
            doc.body.spellcheck = false;
         if (!settings.readonly) {
@@ -13302,6 +13624,17 @@
         self.focus(true);
         self.nodeChanged({initial : true});
         // Add editor specific CSS styles
         if (self.contentStyles.length > 0) {
            contentCssText = '';
            each(self.contentStyles, function(style) {
               contentCssText += style + "\r\n";
            });
            self.dom.addStyle(contentCssText);
         }
         // Load specified content CSS last
         each(self.contentCSS, function(url) {
            self.dom.loadCSS(url);
@@ -13327,6 +13660,10 @@
         var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;
         if (!skip_focus) {
            if (self.lastIERng) {
               selection.setRng(self.lastIERng);
            }
            // Get selected control element
            ieRng = selection.getRng();
            if (ieRng.item) {
@@ -13444,9 +13781,6 @@
         // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
         if (self.initialized) {
            o = o || {};
            // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i>
            selection.normalize();
            // Get start node
            node = selection.getStart() || self.getBody();
@@ -13792,7 +14126,10 @@
         if (!args.no_events)
            self.onSetContent.dispatch(self, args);
         self.selection.normalize();
         // Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise
         if (!self.settings.content_editable || document.activeElement === self.getBody()) {
            self.selection.normalize();
         }
         return args.content;
      },
@@ -13925,14 +14262,16 @@
                  return;
               case 'A':
                  value = dom.getAttrib(elm, 'name');
                  cls = 'mceItemAnchor';
                  if (!dom.getAttrib(elm, 'href', false)) {
                     value = dom.getAttrib(elm, 'name') || elm.id;
                     cls = 'mceItemAnchor';
                  if (value) {
                     if (self.hasVisual)
                        dom.addClass(elm, cls);
                     else
                        dom.removeClass(elm, cls);
                     if (value) {
                        if (self.hasVisual)
                           dom.addClass(elm, cls);
                        else
                           dom.removeClass(elm, cls);
                     }
                  }
                  return;
@@ -13952,13 +14291,12 @@
            // Don't clear the window or document if content editable
            // is enabled since other instances might still be present
            if (!self.settings.content_editable) {
               Event.clear(self.getWin());
               Event.clear(self.getDoc());
               Event.unbind(self.getWin());
               Event.unbind(self.getDoc());
            }
            Event.clear(self.getBody());
            Event.clear(self.formElement);
            Event.unbind(elm);
            Event.unbind(self.getBody());
            Event.clear(elm);
            self.execCallback('remove_instance_callback', self);
            self.onRemove.dispatch(self);
@@ -14160,8 +14498,10 @@
      // Handle legacy handle_event_callback option
      if (settings.handle_event_callback) {
         self.onEvent.add(function(ed, e, o) {
            if (self.execCallback('handle_event_callback', e, ed, o) === false)
               Event.cancel(e);
            if (self.execCallback('handle_event_callback', e, ed, o) === false) {
               e.preventDefault();
               e.stopPropagation();
            }
         });
      }
@@ -14227,6 +14567,15 @@
         self.focus(true);
      };
      function nodeChanged(ed, e) {
         // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
         if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) {
            self.selection.normalize();
         }
         self.nodeChanged();
      }
      // Add DOM events
      each(nativeToDispatcherMap, function(dispatcherName, nativeName) {
         var root = settings.content_editable ? self.getBody() : self.getDoc();
@@ -14261,13 +14610,13 @@
      }
      // Add node change handler
      self.onMouseUp.add(self.nodeChanged);
      self.onMouseUp.add(nodeChanged);
      self.onKeyUp.add(function(ed, e) {
         var keyCode = e.keyCode;
         if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)
            self.nodeChanged();
            nodeChanged(ed, e);
      });
      // Add reset handler
@@ -14694,6 +15043,10 @@
            editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, function() { return value }));
         },
         mceToggleFormat : function(command, ui, value) {
            toggleFormat(value);
         },
         mceSetContent : function(command, ui, value) {
            editor.setContent(value);
         },
@@ -14875,9 +15228,10 @@
      };
      // Create event instances
      onAdd = new Dispatcher(self);
      onUndo = new Dispatcher(self);
      onRedo = new Dispatcher(self);
      onBeforeAdd = new Dispatcher(self);
      onAdd       = new Dispatcher(self);
      onUndo      = new Dispatcher(self);
      onRedo      = new Dispatcher(self);
      // Pass though onAdd event from UndoManager to Editor as onChange
      onAdd.add(function(undoman, level) {
@@ -14966,6 +15320,8 @@
         data : data,
         typing : false,
         onBeforeAdd: onBeforeAdd,
         onAdd : onAdd,
@@ -14982,6 +15338,8 @@
            level = level || {};
            level.content = getContent();
            self.onBeforeAdd.dispatch(self, level);
            // Add undo level if needed
            lastLevel = data[index];
@@ -15077,13 +15435,13 @@
   var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();
   function addRootBlocks() {
      var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped;
      var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped, isInEditorDocument;
      if (!node || node.nodeType !== 1 || !settings.forced_root_block)
         return;
      // Check if node is wrapped in block
      while (node != rootNode) {
      while (node && node != rootNode) {
         if (blockElements[node.nodeName])
            return;
@@ -15105,6 +15463,7 @@
            rng.moveToElementText(node);
         }
         isInEditorDocument = rng.parentElement().ownerDocument === editor.getDoc();
         tmpRng = rng.duplicate();
         tmpRng.collapse(true);
         startOffset = tmpRng.move('character', offset) * -1;
@@ -15135,28 +15494,30 @@
         }
      }
      if (rng.setStart) {
         rng.setStart(startContainer, startOffset);
         rng.setEnd(endContainer, endOffset);
         selection.setRng(rng);
      } else {
         try {
            rng = editor.getDoc().body.createTextRange();
            rng.moveToElementText(rootNode);
            rng.collapse(true);
            rng.moveStart('character', startOffset);
            if (endOffset > 0)
               rng.moveEnd('character', endOffset);
            rng.select();
         } catch (ex) {
            // Ignore
         }
      }
      // Only trigger nodeChange when we wrapped nodes to prevent a forever loop
      if (wrapped) {
         if (rng.setStart) {
            rng.setStart(startContainer, startOffset);
            rng.setEnd(endContainer, endOffset);
            selection.setRng(rng);
         } else {
            // Only select if the previous selection was inside the document to prevent auto focus in quirks mode
            if (isInEditorDocument) {
               try {
                  rng = editor.getDoc().body.createTextRange();
                  rng.moveToElementText(rootNode);
                  rng.collapse(true);
                  rng.moveStart('character', startOffset);
                  if (endOffset > 0)
                     rng.moveEnd('character', endOffset);
                  rng.select();
               } catch (ex) {
                  // Ignore
               }
            }
         }
         editor.nodeChanged();
      }
   };
@@ -15224,28 +15585,40 @@
         return c;
      },
      createControl : function(n) {
         var c, t = this, ed = t.editor;
      createControl : function(name) {
         var ctrl, i, l, self = this, editor = self.editor, factories, ctrlName;
         each(ed.plugins, function(p) {
            if (p.createControl) {
               c = p.createControl(n, t);
               if (c)
                  return false;
            }
         });
         switch (n) {
            case "|":
            case "separator":
               return t.createSeparator();
         // Build control factory cache
         if (!self.controlFactories) {
            self.controlFactories = [];
            each(editor.plugins, function(plugin) {
               if (plugin.createControl) {
                  self.controlFactories.push(plugin);
               }
            });
         }
         if (!c && ed.buttons && (c = ed.buttons[n]))
            return t.createButton(n, c);
         // Create controls by asking cached factories
         factories = self.controlFactories;
         for (i = 0, l = factories.length; i < l; i++) {
            ctrl = factories[i].createControl(name, self);
         return t.add(c);
            if (ctrl) {
               return self.add(ctrl);
            }
         }
         // Create sepearator
         if (name === "|" || name === "separator") {
            return self.createSeparator();
         }
         // Create control from button collection
         if (editor.buttons && (ctrl = editor.buttons[name])) {
            return self.createButton(name, ctrl);
         }
         return self.add(ctrl);
      },
      createDropMenu : function(id, s, cc) {
@@ -15680,6 +16053,7 @@
         MCE_ATTR_RE = /^(src|href|style)$/,
         FALSE = false,
         TRUE = true,
         formatChangeData,
         undef,
         getContentEditable = dom.getContentEditable;
@@ -16561,7 +16935,7 @@
                  matchedFormatNames.push(name);
               }
            }
         });
         }, dom.getRoot());
         return matchedFormatNames;
      };
@@ -16590,6 +16964,61 @@
         return FALSE;
      };
      function formatChanged(formats, callback) {
         var currentFormats;
         // Setup format node change logic
         if (!formatChangeData) {
            formatChangeData = {};
            currentFormats = {};
            ed.onNodeChange.addToTop(function(ed, cm, node) {
               var parents = getParents(node), matchedFormats = {};
               // Check for new formats
               each(formatChangeData, function(callbacks, format) {
                  each(parents, function(node) {
                     if (matchNode(node, format, {}, true)) {
                        if (!currentFormats[format]) {
                           // Execute callbacks
                           each(callbacks, function(callback) {
                              callback(true, {node: node, format: format, parents: parents});
                           });
                           currentFormats[format] = callbacks;
                        }
                        matchedFormats[format] = callbacks;
                        return false;
                     }
                  });
               });
               // Check if current formats still match
               each(currentFormats, function(callbacks, format) {
                  if (!matchedFormats[format]) {
                     delete currentFormats[format];
                     each(callbacks, function(callback) {
                        callback(false, {node: node, format: format, parents: parents});
                     });
                  }
               });
            });
         }
         // Add format listeners
         each(formats.split(','), function(format) {
            if (!formatChangeData[format]) {
               formatChangeData[format] = [];
            }
            formatChangeData[format].push(callback);
         });
         return this;
      };
      // Expose to public
      tinymce.extend(this, {
         get : get,
@@ -16600,7 +17029,8 @@
         match : match,
         matchAll : matchAll,
         matchNode : matchNode,
         canApply : canApply
         canApply : canApply,
         formatChanged: formatChanged
      });
      // Initialize
@@ -17506,6 +17936,21 @@
            }
         };
         // Checks if the parent caret container node isn't empty if that is the case it
         // will remove the bogus state on all children that isn't empty
         function unmarkBogusCaretParents() {
            var i, caretContainer, node;
            caretContainer = getParentCaretContainer(selection.getStart());
            if (caretContainer && !dom.isEmpty(caretContainer)) {
               tinymce.walk(caretContainer, function(node) {
                  if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
                     dom.setAttrib(node, 'data-mce-bogus', null);
                  }
               }, 'childNodes');
            }
         };
         // Only bind the caret events once
         if (!self._hasCaretEvents) {
            // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
@@ -17525,6 +17970,7 @@
            tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
               ed[name].addToTop(function() {
                  removeCaretContainer();
                  unmarkBogusCaretParents();
               });
            });
@@ -17535,16 +17981,12 @@
               if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
                  removeCaretContainer(getParentCaretContainer(selection.getStart()));
               }
               unmarkBogusCaretParents();
            });
            // Remove bogus state if they got filled by contents using editor.selection.setContent
            selection.onSetContent.add(function() {
               dom.getParent(selection.getStart(), function(node) {
                  if (node.id !== caretContainerId && dom.getAttrib(node, 'data-mce-bogus') && !dom.isEmpty(node)) {
                     dom.setAttrib(node, 'data-mce-bogus', null);
                  }
               });
            });
            selection.onSetContent.add(unmarkBogusCaretParents);
            self._hasCaretEvents = true;
         }
@@ -17661,7 +18103,7 @@
   var TreeWalker = tinymce.dom.TreeWalker;
   tinymce.EnterKey = function(editor) {
      var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;
      var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager, nonEmptyElementsMap = editor.schema.getNonEmptyElements();
      function handleEnterKey(evt) {
         var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode,
@@ -17671,11 +18113,48 @@
         function canSplitBlock(node) {
            return node &&
               dom.isBlock(node) &&
               !/^(TD|TH|CAPTION)$/.test(node.nodeName) &&
               !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
               !/^(fixed|absolute)/i.test(node.style.position) && 
               dom.getContentEditable(node) !== "true";
         };
         // Renders empty block on IE
         function renderBlockOnIE(block) {
            var oldRng;
            if (tinymce.isIE && dom.isBlock(block)) {
               oldRng = selection.getRng();
               block.appendChild(dom.create('span', null, '\u00a0'));
               selection.select(block);
               block.lastChild.outerHTML = '';
               selection.setRng(oldRng);
            }
         };
         // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
         function trimInlineElementsOnLeftSideOfBlock(block) {
            var node = block, firstChilds = [], i;
            // Find inner most first child ex: <p><i><b>*</b></i></p>
            while (node = node.firstChild) {
               if (dom.isBlock(node)) {
                  return;
               }
               if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
                  firstChilds.push(node);
               }
            }
            i = firstChilds.length;
            while (i--) {
               node = firstChilds[i];
               if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
                  dom.remove(node);
               }
            }
         };
         // Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
         function moveToCaretPosition(root) {
            var walker, node, rng, y, viewPort, lastNode = root, tempElm;
@@ -17692,7 +18171,7 @@
                     break;
                  }
                  if (/^(BR|IMG)$/.test(node.nodeName)) {
                  if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
                     rng.setStartBefore(node);
                     rng.setEndBefore(node);
                     break;
@@ -17789,6 +18268,11 @@
               return true;
            }
            // If the caret if before the first element in parentBlock
            if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
               return true;
            }
            // Caret can be before/after a table
            if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
               return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
@@ -17796,20 +18280,34 @@
            // Walk the DOM and look for text nodes or non empty elements
            walker = new TreeWalker(container, parentBlock);
            while (node = (start ? walker.prev() : walker.next())) {
            // If caret is in beginning or end of a text block then jump to the next/previous node
            if (container.nodeType == 3) {
               if (start && offset == 0) {
                  walker.prev();
               } else if (!start && offset == container.nodeValue.length) {
                  walker.next();
               }
            }
            while (node = walker.current()) {
               if (node.nodeType === 1) {
                  // Ignore bogus elements
                  if (node.getAttribute('data-mce-bogus')) {
                     continue;
                  }
                  // Keep empty elements like <img />
                  name = node.nodeName.toLowerCase();
                  if (name === 'IMG') {
                     return false;
                  if (!node.getAttribute('data-mce-bogus')) {
                     // Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
                     name = node.nodeName.toLowerCase();
                     if (nonEmptyElementsMap[name] && name !== 'br') {
                        return false;
                     }
                  }
               } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
                  return false;
               }
               if (start) {
                  walker.prev();
               } else {
                  walker.next();
               }
            }
@@ -17894,6 +18392,7 @@
            } else if (isFirstOrLastLi()) {
               // Last LI in list then temove LI and add text block after list
               dom.insertAfter(newBlock, containerBlock);
               renderBlockOnIE(newBlock);
            } else {
               // Middle LI in list the split the list and insert a text block in the middle
               // Extract after fragment and insert it after the current block
@@ -17985,6 +18484,22 @@
            return parent !== root ? editableRoot : root;
         };
         // Adds a BR at the end of blocks that only contains an IMG or INPUT since these might be floated and then they won't expand the block
         function addBrToBlockIfNeeded(block) {
            var lastChild;
            // IE will render the blocks correctly other browsers needs a BR
            if (!tinymce.isIE) {
               block.normalize(); // Remove empty text nodes that got left behind by the extract
               // Check if the block is empty or contains a floated last child
               lastChild = block.lastChild;
               if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
                  dom.add(block, 'br');
               }
            }
         };
         // Delete any selected contents
         if (!rng.collapsed) {
            editor.execCommand('Delete');
@@ -18007,7 +18522,11 @@
         if (container.nodeType == 1 && container.hasChildNodes()) {
            isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
            container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
            offset = 0;
            if (isAfterLastNodeInContainer && container.nodeType == 3) {
               offset = container.nodeValue.length;
            } else {
               offset = 0;
            }
         }
         // Get editable root node normaly the body element but sometimes a div or span
@@ -18088,9 +18607,12 @@
            } else {
               dom.insertAfter(newBlock, parentBlock);
            }
            moveToCaretPosition(newBlock);
         } else if (isCaretAtStartOrEndOfBlock(true)) {
            // Insert new block before
            newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
            renderBlockOnIE(newBlock);
         } else {
            // Extract after fragment and insert it after the current block
            tmpRng = rng.cloneRange();
@@ -18099,10 +18621,12 @@
            trimLeadingLineBreaks(fragment);
            newBlock = fragment.firstChild;
            dom.insertAfter(fragment, parentBlock);
            trimInlineElementsOnLeftSideOfBlock(newBlock);
            addBrToBlockIfNeeded(parentBlock);
            moveToCaretPosition(newBlock);
         }
         dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
         moveToCaretPosition(newBlock);
         undoManager.add();
      }