(function(tinymce) {
var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;
/**
* Fixes a WebKit bug when deleting contents using backspace or delete key.
* WebKit will produce a span element if you delete across two block elements.
*
* Example:
*
a
|b
*
* Will produce this on backspace:
* ab
*
* This fixes the backspace to produce:
* a|b
*
* See bug: https://bugs.webkit.org/show_bug.cgi?id=45784
*
* This code is a bit of a hack and hopefully it will be fixed soon in WebKit.
*/
function cleanupStylesWhenDeleting(ed) {
var dom = ed.dom, selection = ed.selection;
ed.onKeyDown.add(function(ed, e) {
var rng, blockElm, node, clonedSpan, isDelete;
isDelete = e.keyCode == DELETE;
if (isDelete || e.keyCode == BACKSPACE) {
e.preventDefault();
rng = selection.getRng();
// Find root block
blockElm = dom.getParent(rng.startContainer, dom.isBlock);
// On delete clone the root span of the next block element
if (isDelete)
blockElm = dom.getNext(blockElm, dom.isBlock);
// Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace
if (blockElm) {
node = blockElm.firstChild;
// Ignore empty text nodes
while (node && node.nodeType == 3 && node.nodeValue.length == 0)
node = node.nextSibling;
if (node && node.nodeName === 'SPAN') {
clonedSpan = node.cloneNode(false);
}
}
// Do the backspace/delete actiopn
ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
// Find all odd apple-style-spans
blockElm = dom.getParent(rng.startContainer, dom.isBlock);
tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {
var bm = selection.getBookmark();
if (clonedSpan) {
dom.replace(clonedSpan.cloneNode(false), span, true);
} else {
dom.remove(span, true);
}
// Restore the selection
selection.moveToBookmark(bm);
});
}
});
};
/**
* WebKit and IE doesn't empty the editor if you select all contents and hit backspace or delete. This fix will check if the body is empty
* like a or
and then forcefully remove all contents.
*/
function emptyEditorWhenDeleting(ed) {
ed.onKeyUp.add(function(ed, e) {
var keyCode = e.keyCode;
if (keyCode == DELETE || keyCode == BACKSPACE) {
if (ed.dom.isEmpty(ed.getBody())) {
ed.setContent('', {format : 'raw'});
ed.nodeChanged();
return;
}
}
});
};
/**
* WebKit on MacOS X has a weird issue where it some times fails to properly convert keypresses to input method keystrokes.
* So a fix where we just get the range and set the range back seems to do the trick.
*/
function inputMethodFocus(ed) {
ed.dom.bind(ed.getDoc(), 'focusin', function() {
ed.selection.setRng(ed.selection.getRng());
});
};
/**
* Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the
* browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is
* left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js
* addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other
* browsers
*/
function removeHrOnBackspace(ed) {
ed.onKeyDown.add(function(ed, e) {
if (e.keyCode === BACKSPACE) {
if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
var node = ed.selection.getNode();
var previousSibling = node.previousSibling;
if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
ed.dom.remove(previousSibling);
tinymce.dom.Event.cancel(e);
}
}
}
})
}
/**
* Firefox 3.x has an issue where the body element won't get proper focus if you click out
* side it's rectangle.
*/
function focusBody(ed) {
// Fix for a focus bug in FF 3.x where the body element
// wouldn't get proper focus if the user clicked on the HTML element
if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
ed.onMouseDown.add(function(ed, e) {
if (e.target.nodeName === "HTML") {
var body = ed.getBody();
// Blur the body it's focused but not correctly focused
body.blur();
// Refocus the body after a little while
setTimeout(function() {
body.focus();
}, 0);
}
});
}
};
/**
* WebKit has a bug where it isn't possible to select image, hr or anchor elements
* by clicking on them so we need to fake that.
*/
function selectControlElements(ed) {
ed.onClick.add(function(ed, e) {
e = e.target;
// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
// WebKit can't even do simple things like selecting an image
// Needs tobe the setBaseAndExtend or it will fail to select floated images
if (/^(IMG|HR)$/.test(e.nodeName))
ed.selection.getSel().setBaseAndExtent(e, 0, e, 1);
if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor'))
ed.selection.select(e);
ed.nodeChanged();
});
};
/**
* Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
* event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
*/
function selectionChangeNodeChanged(ed) {
var lastRng, selectionTimer;
ed.dom.bind(ed.getDoc(), 'selectionchange', function() {
if (selectionTimer) {
clearTimeout(selectionTimer);
selectionTimer = 0;
}
selectionTimer = window.setTimeout(function() {
var rng = ed.selection.getRng();
// Compare the ranges to see if it was a real change or not
if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
ed.nodeChanged();
lastRng = rng;
}
}, 50);
});
}
/**
* Screen readers on IE needs to have the role application set on the body.
*/
function ensureBodyHasRoleApplication(ed) {
document.body.setAttribute("role", "application");
}
tinymce.create('tinymce.util.Quirks', {
Quirks: function(ed) {
// WebKit
if (tinymce.isWebKit) {
cleanupStylesWhenDeleting(ed);
emptyEditorWhenDeleting(ed);
inputMethodFocus(ed);
selectControlElements(ed);
// iOS
if (tinymce.isIDevice) {
selectionChangeNodeChanged(ed);
}
}
// IE
if (tinymce.isIE) {
removeHrOnBackspace(ed);
emptyEditorWhenDeleting(ed);
ensureBodyHasRoleApplication(ed);
}
// Gecko
if (tinymce.isGecko) {
removeHrOnBackspace(ed);
focusBody(ed);
}
}
});
})(tinymce);