4 * Copyright, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://www.tinymce.com/license
8 * Contributing: http://www.tinymce.com/contributing
14 * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes.
16 * @class tinymce.util.Quirks
18 define("tinymce/util/Quirks", [
20 "tinymce/dom/RangeUtils",
22 "tinymce/html/Entities",
25 ], function(VK, RangeUtils, Node, Entities, Env, Tools) {
26 return function(editor) {
27 var each = Tools.each;
28 var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
29 settings = editor.settings, parser = editor.parser, serializer = editor.serializer;
30 var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit;
33 * Executes a command with a specific state this can be to enable/disable browser editing features.
35 function setEditorCommandState(cmd, state) {
37 editor.getDoc().execCommand(cmd, false, state);
44 * Returns current IE document mode.
46 function getDocumentMode() {
47 var documentMode = editor.getDoc().documentMode;
49 return documentMode ? documentMode : 6;
53 * Returns true/false if the event is prevented or not.
56 * @param {Event} e Event object.
57 * @return {Boolean} true/false if the event is prevented or not.
59 function isDefaultPrevented(e) {
60 return e.isDefaultPrevented();
64 * Fixes a WebKit bug when deleting contents using backspace or delete key.
65 * WebKit will produce a span element if you delete across two block elements.
70 * Will produce this on backspace:
71 * <h1>a<span class="Apple-style-span" style="<all runtime styles>">b</span></p>
73 * This fixes the backspace to produce:
76 * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
78 * This code is a bit of a hack and hopefully it will be fixed soon in WebKit.
80 function cleanupStylesWhenDeleting() {
81 function removeMergedFormatSpans(isDelete) {
82 var rng, blockElm, wrapperElm, bookmark, container, offset, elm;
84 function isAtStartOrEndOfElm() {
85 if (container.nodeType == 3) {
86 if (isDelete && offset == container.length) {
90 if (!isDelete && offset === 0) {
96 rng = selection.getRng();
97 var tmpRng = [rng.startContainer, rng.startOffset, rng.endContainer, rng.endOffset];
103 container = rng[(isDelete ? 'start' : 'end') + 'Container'];
104 offset = rng[(isDelete ? 'start' : 'end') + 'Offset'];
106 if (container.nodeType == 3) {
107 blockElm = dom.getParent(rng.startContainer, dom.isBlock);
109 // On delete clone the root span of the next block element
111 blockElm = dom.getNext(blockElm, dom.isBlock);
114 if (blockElm && (isAtStartOrEndOfElm() || !rng.collapsed)) {
115 // Wrap children of block in a EM and let WebKit stick is
116 // runtime styles junk into that EM
117 wrapperElm = dom.create('em', {'id': '__mceDel'});
119 each(Tools.grep(blockElm.childNodes), function(node) {
120 wrapperElm.appendChild(node);
123 blockElm.appendChild(wrapperElm);
127 // Do the backspace/delete action
128 rng = dom.createRng();
129 rng.setStart(tmpRng[0], tmpRng[1]);
130 rng.setEnd(tmpRng[2], tmpRng[3]);
131 selection.setRng(rng);
132 editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
134 // Remove temp wrapper element
136 bookmark = selection.getBookmark();
138 while ((elm = dom.get('__mceDel'))) {
139 dom.remove(elm, true);
142 selection.moveToBookmark(bookmark);
146 editor.on('keydown', function(e) {
149 isDelete = e.keyCode == DELETE;
150 if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
152 removeMergedFormatSpans(isDelete);
156 editor.addCommand('Delete', function() {removeMergedFormatSpans();});
160 * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors.
171 function emptyEditorWhenDeleting() {
172 function serializeRng(rng) {
173 var body = dom.create("body");
174 var contents = rng.cloneContents();
175 body.appendChild(contents);
176 return selection.serializer.serialize(body, {format: 'html'});
179 function allContentsSelected(rng) {
180 var selection = serializeRng(rng);
182 var allRng = dom.createRng();
183 allRng.selectNode(editor.getBody());
185 var allSelection = serializeRng(allRng);
186 return selection === allSelection;
189 editor.on('keydown', function(e) {
190 var keyCode = e.keyCode, isCollapsed;
192 // Empty the editor if it's needed for example backspace at <p><b>|</b></p>
193 if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
194 isCollapsed = editor.selection.isCollapsed();
196 // Selection is collapsed but the editor isn't empty
197 if (isCollapsed && !dom.isEmpty(editor.getBody())) {
201 // IE deletes all contents correctly when everything is selected
202 if (isIE && !isCollapsed) {
206 // Selection isn't collapsed but not all the contents is selected
207 if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
211 // Manually empty the editor
213 editor.setContent('');
214 editor.selection.setCursorLocation(editor.getBody(), 0);
215 editor.nodeChanged();
221 * WebKit doesn't select all the nodes in the body when you press Ctrl+A.
222 * This selects the whole body so that backspace/delete logic will delete everything
224 function selectAll() {
225 editor.on('keydown', function(e) {
226 if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
228 editor.execCommand('SelectAll');
234 * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
235 * The IME on Mac doesn't initialize when it doesn't fire a proper focus event.
237 * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until
238 * you enter a character into the editor.
240 * It also happens when the first focus in made to the body.
242 * See: https://bugs.webkit.org/show_bug.cgi?id=83566
244 function inputMethodFocus() {
245 if (!editor.settings.content_editable) {
246 // Case 1 IME doesn't initialize if you focus the document
247 dom.bind(editor.getDoc(), 'focusin', function() {
248 selection.setRng(selection.getRng());
251 // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
252 dom.bind(editor.getDoc(), 'mousedown', function(e) {
253 if (e.target == editor.getDoc().documentElement) {
254 editor.getWin().focus();
255 selection.setRng(selection.getRng());
262 * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
263 * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
264 * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
265 * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
268 function removeHrOnBackspace() {
269 editor.on('keydown', function(e) {
270 if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
271 if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
272 var node = selection.getNode();
273 var previousSibling = node.previousSibling;
275 if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
276 dom.remove(previousSibling);
285 * Firefox 3.x has an issue where the body element won't get proper focus if you click out
286 * side it's rectangle.
288 function focusBody() {
289 // Fix for a focus bug in FF 3.x where the body element
290 // wouldn't get proper focus if the user clicked on the HTML element
291 if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
292 editor.on('mousedown', function(e) {
293 if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
294 var body = editor.getBody();
296 // Blur the body it's focused but not correctly focused
299 // Refocus the body after a little while
300 setTimeout(function() {
309 * WebKit has a bug where it isn't possible to select image, hr or anchor elements
310 * by clicking on them so we need to fake that.
312 function selectControlElements() {
313 editor.on('click', function(e) {
316 // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
317 // WebKit can't even do simple things like selecting an image
318 // Needs tobe the setBaseAndExtend or it will fail to select floated images
319 if (/^(IMG|HR)$/.test(e.nodeName)) {
320 selection.getSel().setBaseAndExtent(e, 0, e, 1);
323 if (e.nodeName == 'A' && dom.hasClass(e, 'mce-item-anchor')) {
327 editor.nodeChanged();
332 * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements.
334 * Fixes do backspace/delete on this:
335 * <p>bla[ck</p><p style="color:red">r]ed</p>
341 * <p style="color:red">bla|ed</p>
343 function removeStylesWhenDeletingAcrossBlockElements() {
344 function getAttributeApplyFunction() {
345 var template = dom.getAttribs(selection.getStart().cloneNode(false));
348 var target = selection.getStart();
350 if (target !== editor.getBody()) {
351 dom.setAttrib(target, "style", null);
353 each(template, function(attr) {
354 target.setAttributeNode(attr.cloneNode(true));
360 function isSelectionAcrossElements() {
361 return !selection.isCollapsed() &&
362 dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock);
365 editor.on('keypress', function(e) {
368 if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
369 applyAttributes = getAttributeApplyFunction();
370 editor.getDoc().execCommand('delete', false, null);
377 dom.bind(editor.getDoc(), 'cut', function(e) {
380 if (!isDefaultPrevented(e) && isSelectionAcrossElements()) {
381 applyAttributes = getAttributeApplyFunction();
383 setTimeout(function() {
391 * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
392 * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
394 function selectionChangeNodeChanged() {
395 var lastRng, selectionTimer;
397 editor.on('selectionchange', function() {
398 if (selectionTimer) {
399 clearTimeout(selectionTimer);
403 selectionTimer = window.setTimeout(function() {
404 var rng = selection.getRng();
406 // Compare the ranges to see if it was a real change or not
407 if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
408 editor.nodeChanged();
416 * Screen readers on IE needs to have the role application set on the body.
418 function ensureBodyHasRoleApplication() {
419 document.body.setAttribute("role", "application");
423 * Backspacing into a table behaves differently depending upon browser type.
424 * Therefore, disable Backspace when cursor immediately follows a table.
426 function disableBackspaceIntoATable() {
427 editor.on('keydown', function(e) {
428 if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
429 if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
430 var previousSibling = selection.getNode().previousSibling;
431 if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") {
441 * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this
442 * logic adds a \n before the BR so that it will get rendered.
444 function addNewLinesBeforeBrInPre() {
445 // IE8+ rendering mode does the right thing with BR in PRE
446 if (getDocumentMode() > 7) {
450 // Enable display: none in area and add a specific class that hides all BR elements in PRE to
451 // avoid the caret from getting stuck at the BR elements while pressing the right arrow key
452 setEditorCommandState('RespectVisibilityInDesign', true);
453 editor.contentStyles.push('.mceHideBrInPre pre br {display: none}');
454 dom.addClass(editor.getBody(), 'mceHideBrInPre');
456 // Adds a \n before all BR elements in PRE to get them visual
457 parser.addNodeFilter('pre', function(nodes) {
458 var i = nodes.length, brNodes, j, brElm, sibling;
461 brNodes = nodes[i].getAll('br');
466 // Add \n before BR in PRE elements on older IE:s so the new lines get rendered
467 sibling = brElm.prev;
468 if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') {
469 sibling.value += '\n';
471 brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n';
477 // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible
478 serializer.addNodeFilter('pre', function(nodes) {
479 var i = nodes.length, brNodes, j, brElm, sibling;
482 brNodes = nodes[i].getAll('br');
486 sibling = brElm.prev;
487 if (sibling && sibling.type == 3) {
488 sibling.value = sibling.value.replace(/\r?\n$/, '');
496 * Moves style width/height to attribute width/height when the user resizes an image on IE.
498 function removePreSerializedStylesWhenSelectingControls() {
499 dom.bind(editor.getBody(), 'mouseup', function() {
500 var value, node = selection.getNode();
502 // Moved styles to attributes on IMG eements
503 if (node.nodeName == 'IMG') {
504 // Convert style width to width attribute
505 if ((value = dom.getStyle(node, 'width'))) {
506 dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, ''));
507 dom.setStyle(node, 'width', '');
510 // Convert style height to height attribute
511 if ((value = dom.getStyle(node, 'height'))) {
512 dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, ''));
513 dom.setStyle(node, 'height', '');
520 * Backspace or delete on WebKit will combine all visual styles in a span if the last character is deleted.
522 * For example backspace on:
526 * <p><span style="font-weight: bold">|<br></span></p>
528 * When it should produce:
529 * <p><b>|<br></b></p>
531 * See: https://bugs.webkit.org/show_bug.cgi?id=81656
533 function keepInlineElementOnDeleteBackspace() {
534 editor.on('keydown', function(e) {
535 var isDelete, rng, container, offset, brElm, sibling, collapsed, nonEmptyElements;
537 isDelete = e.keyCode == DELETE;
538 if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
539 rng = selection.getRng();
540 container = rng.startContainer;
541 offset = rng.startOffset;
542 collapsed = rng.collapsed;
544 // Override delete if the start container is a text node and is at the beginning of text or
545 // just before/after the last character to be deleted in collapsed mode
546 if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) ||
547 (collapsed && offset === (isDelete ? 0 : 1)))) {
548 // Edge case when deleting <p><b><img> |x</b></p>
549 sibling = container.previousSibling;
550 if (sibling && sibling.nodeName == "IMG") {
554 nonEmptyElements = editor.schema.getNonEmptyElements();
556 // Prevent default logic since it's broken
559 // Insert a BR before the text node this will prevent the containing element from being deleted/converted
560 brElm = dom.create('br', {id: '__tmp'});
561 container.parentNode.insertBefore(brElm, container);
563 // Do the browser delete
564 editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
566 // Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>
567 container = selection.getRng().startContainer;
568 sibling = container.previousSibling;
569 if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) &&
570 !nonEmptyElements[sibling.nodeName.toLowerCase()]) {
574 // Remove the temp element we inserted
582 * Removes a blockquote when backspace is pressed at the beginning of it.
585 * <blockquote><p>|x</p></blockquote>
590 function removeBlockQuoteOnBackSpace() {
591 // Add block quote deletion handler
592 editor.on('keydown', function(e) {
593 var rng, container, offset, root, parent;
595 if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) {
599 rng = selection.getRng();
600 container = rng.startContainer;
601 offset = rng.startOffset;
602 root = dom.getRoot();
605 if (!rng.collapsed || offset !== 0) {
609 while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) {
610 parent = parent.parentNode;
613 // Is the cursor at the beginning of a blockquote?
614 if (parent.tagName === 'BLOCKQUOTE') {
615 // Remove the blockquote
616 editor.formatter.toggle('blockquote', null, parent);
618 // Move the caret to the beginning of container
619 rng = dom.createRng();
620 rng.setStart(container, 0);
621 rng.setEnd(container, 0);
622 selection.setRng(rng);
628 * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc.
630 function setGeckoEditingOptions() {
632 editor._refreshContentEditable();
634 setEditorCommandState("StyleWithCSS", false);
635 setEditorCommandState("enableInlineTableEditing", false);
637 if (!settings.object_resizing) {
638 setEditorCommandState("enableObjectResizing", false);
642 if (!settings.readonly) {
643 editor.on('BeforeExecCommand MouseDown', setOpts);
648 * Fixes a gecko link bug, when a link is placed at the end of block elements there is
649 * no way to move the caret behind the link. This fix adds a bogus br element after the link.
652 * <p><b><a href="#">x</a></b></p>
655 * <p><b><a href="#">x</a></b><br></p>
657 function addBrAfterLastLinks() {
658 function fixLinks() {
659 each(dom.select('a'), function(node) {
660 var parentNode = node.parentNode, root = dom.getRoot();
662 if (parentNode.lastChild === node) {
663 while (parentNode && !dom.isBlock(parentNode)) {
664 if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) {
668 parentNode = parentNode.parentNode;
671 dom.add(parentNode, 'br', {'data-mce-bogus': 1});
676 editor.on('SetContent ExecCommand', function(e) {
677 if (e.type == "setcontent" || e.command === 'mceInsertLink') {
684 * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by
685 * default we want to change that behavior.
687 function setDefaultBlockType() {
688 if (settings.forced_root_block) {
689 editor.on('init', function() {
690 setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
696 * Removes ghost selections from images/tables on Gecko.
698 function removeGhostSelection() {
699 editor.on('Undo Redo SetContent', function(e) {
701 editor.execCommand('mceRepaint');
707 * Deletes the selected image on IE instead of navigating to previous page.
709 function deleteControlItemOnBackSpace() {
710 editor.on('keydown', function(e) {
713 if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) {
714 rng = editor.getDoc().selection.createRange();
715 if (rng && rng.item) {
717 editor.undoManager.beforeChange();
718 dom.remove(rng.item(0));
719 editor.undoManager.add();
726 * IE10 doesn't properly render block elements with the right height until you add contents to them.
727 * This fixes that by adding a padding-right to all empty text block elements.
728 * See: https://connect.microsoft.com/IE/feedback/details/743881
730 function renderEmptyBlocksFix() {
734 if (getDocumentMode() >= 10) {
736 each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) {
737 emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty';
740 editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}');
745 * Old IE versions can't retain contents within noscript elements so this logic will store the contents
746 * as a attribute and the insert that value as it's raw text when the DOM is serialized.
748 function keepNoScriptContents() {
749 if (getDocumentMode() < 9) {
750 parser.addNodeFilter('noscript', function(nodes) {
751 var i = nodes.length, node, textNode;
755 textNode = node.firstChild;
758 node.attr('data-mce-innertext', textNode.value);
763 serializer.addNodeFilter('noscript', function(nodes) {
764 var i = nodes.length, node, textNode, value;
768 textNode = nodes[i].firstChild;
771 textNode.value = Entities.decode(textNode.value);
773 // Old IE can't retain noscript value so an attribute is used to store it
774 value = node.attributes.map['data-mce-innertext'];
776 node.attr('data-mce-innertext', null);
777 textNode = new Node('#text', 3);
778 textNode.value = value;
780 node.append(textNode);
789 * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode.
791 function fixCaretSelectionOfDocumentElementOnIe() {
792 var doc = dom.doc, body = doc.body, started, startRng, htmlElm;
794 // Return range from point or null if it failed
795 function rngFromPoint(x, y) {
796 var rng = body.createTextRange();
799 rng.moveToPoint(x, y);
801 // IE sometimes throws and exception, so lets just ignore it
808 // Fires while the selection is changing
809 function selectionChange(e) {
812 // Check if the button is down or not
814 // Create range from mouse position
815 pointRng = rngFromPoint(e.x, e.y);
818 // Check if pointRange is before/after selection then change the endPoint
819 if (pointRng.compareEndPoints('StartToStart', startRng) > 0) {
820 pointRng.setEndPoint('StartToStart', startRng);
822 pointRng.setEndPoint('EndToEnd', startRng);
833 function endSelection() {
834 var rng = doc.selection.createRange();
836 // If the range is collapsed then use the last start range
837 if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) {
841 dom.unbind(doc, 'mouseup', endSelection);
842 dom.unbind(doc, 'mousemove', selectionChange);
843 startRng = started = 0;
846 // Make HTML element unselectable since we are going to handle selection by hand
847 doc.documentElement.unselectable = true;
849 // Detect when user selects outside BODY
850 dom.bind(doc, 'mousedown contextmenu', function(e) {
851 if (e.target.nodeName === 'HTML') {
856 // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML
857 htmlElm = doc.documentElement;
858 if (htmlElm.scrollHeight > htmlElm.clientHeight) {
863 // Setup start position
864 startRng = rngFromPoint(e.x, e.y);
866 // Listen for selection change events
867 dom.bind(doc, 'mouseup', endSelection);
868 dom.bind(doc, 'mousemove', selectionChange);
878 * Fixes selection issues on Gecko where the caret can be placed between two inline elements like <b>a</b>|<b>b</b>
879 * this fix will lean the caret right into the closest inline element.
881 function normalizeSelection() {
882 // 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
883 editor.on('keyup focusin', function(e) {
884 if (e.keyCode != 65 || !VK.metaKeyPressed(e)) {
885 selection.normalize();
891 * Forces Gecko to render a broken image icon if it fails to load an image.
893 function showBrokenImageIcon() {
894 editor.contentStyles.push(
895 'img:-moz-broken {' +
896 '-moz-force-broken-image-icon:1;' +
904 * iOS has a bug where it's impossible to type if the document has a touchstart event
905 * bound and the user touches the document while having the on screen keyboard visible.
907 * The touch event moves the focus to the parent document while having the caret inside the iframe
908 * this fix moves the focus back into the iframe document.
910 function restoreFocusOnKeyDown() {
911 if (!editor.inline) {
912 editor.on('keydown', function() {
913 if (document.activeElement == document.body) {
914 editor.getWin().focus();
921 disableBackspaceIntoATable();
922 removeBlockQuoteOnBackSpace();
923 emptyEditorWhenDeleting();
924 normalizeSelection();
928 keepInlineElementOnDeleteBackspace();
929 cleanupStylesWhenDeleting();
931 selectControlElements();
932 setDefaultBlockType();
936 selectionChangeNodeChanged();
937 restoreFocusOnKeyDown();
944 if (isIE && Env.ie < 11) {
945 removeHrOnBackspace();
946 ensureBodyHasRoleApplication();
947 addNewLinesBeforeBrInPre();
948 removePreSerializedStylesWhenSelectingControls();
949 deleteControlItemOnBackSpace();
950 renderEmptyBlocksFix();
951 keepNoScriptContents();
952 fixCaretSelectionOfDocumentElementOnIe();
957 removeHrOnBackspace();
959 removeStylesWhenDeletingAcrossBlockElements();
960 setGeckoEditingOptions();
961 addBrAfterLastLinks();
962 removeGhostSelection();
963 showBrokenImageIcon();