0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-22 15:23:29 -05:00

Workaround WebKit/IE can't focus empty text nodes.

This commit is contained in:
Neil Jenkins 2012-04-02 15:53:24 +10:00
parent 6480739143
commit 543abca022
2 changed files with 69 additions and 5 deletions

View file

@ -19,11 +19,15 @@
var win = doc.defaultView, var win = doc.defaultView,
body = doc.body; body = doc.body;
// Browser sniffing. Unfortunately necessary.
var isOpera = !!win.opera; var isOpera = !!win.opera;
var isIE = !!win.ie; var isIE = !!win.ie;
var isGecko = /Gecko\//.test( navigator.userAgent ); var isGecko = /Gecko\//.test( navigator.userAgent );
var isWebKit = /WebKit/.test( navigator.userAgent );
var isIOS = /iP(?:ad|hone|od)/.test( navigator.userAgent ); var isIOS = /iP(?:ad|hone|od)/.test( navigator.userAgent );
var useTextFixer = isIE || isOpera; var useTextFixer = isIE || isOpera;
var cantFocusEmptyTextNodes = isIE || isWebKit;
// --- DOM Sugar --- // --- DOM Sugar ---
@ -150,7 +154,42 @@
var lastFocusNode; var lastFocusNode;
var path = ''; var path = '';
// --- Workaround for browsers that can't focus empty text nodes ---
// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256
var placeholderTextNode = null;
var setPlaceholderTextNode = function ( node ) {
if ( placeholderTextNode ) {
removePlaceholderTextNode( getSelection(), true );
}
placeholderTextNode = node;
};
var removePlaceholderTextNode = function ( range, force ) {
var node = placeholderTextNode,
index;
if ( !force && node.data === '\u200B' &&
( range.startContainer === node || range.endContainer === node ) ) {
return;
}
placeholderTextNode = null;
if ( node.parentNode ) {
while ( ( index = node.data.indexOf( '\u200B' ) ) > -1 ) {
node.deleteData( index, 1 );
}
if ( !node.data && !node.nextSibling && !node.previousSibling &&
node.parentNode.isInline() ) {
node.parentNode.detach();
}
}
};
// --- Path change events ---
var updatePath = function ( range, force ) { var updatePath = function ( range, force ) {
if ( placeholderTextNode ) {
removePlaceholderTextNode( range );
}
var anchor = range.startContainer, var anchor = range.startContainer,
focus = range.endContainer, focus = range.endContainer,
newPath; newPath;
@ -457,7 +496,8 @@
if ( range.collapsed ) { if ( range.collapsed ) {
var el = createElement( tag, attributes ).fixCursor(); var el = createElement( tag, attributes ).fixCursor();
range._insertNode( el ); range._insertNode( el );
range.selectNodeContents( el ); range.setStart( el.firstChild, el.firstChild.length );
range.collapse( true );
} }
// Otherwise we find all the textnodes in the range (splitting // Otherwise we find all the textnodes in the range (splitting
// partially selected nodes) and if they're not already formatted // partially selected nodes) and if they're not already formatted
@ -526,8 +566,17 @@
// We need a node in the selection to break the surrounding // We need a node in the selection to break the surrounding
// formatted text. // formatted text.
var fixer;
if ( range.collapsed ) { if ( range.collapsed ) {
range._insertNode( doc.createTextNode( '' ) ); if ( cantFocusEmptyTextNodes ) {
fixer = doc.createTextNode( '\u200B' );
setTimeout( function () {
setPlaceholderTextNode( fixer );
}, 0 );
} else {
fixer = doc.createTextNode( '' );
}
range._insertNode( fixer );
} }
// Find block-level ancestor of selection // Find block-level ancestor of selection
@ -609,6 +658,9 @@
// Merge adjacent inlines: // Merge adjacent inlines:
range = getRangeAndRemoveBookmark(); range = getRangeAndRemoveBookmark();
if ( fixer ) {
range.collapse( false );
}
var _range = { var _range = {
startContainer: range.startContainer, startContainer: range.startContainer,
startOffset: range.startOffset, startOffset: range.startOffset,
@ -1485,6 +1537,8 @@
win.editor = { win.editor = {
_setPlaceholderTextNode: setPlaceholderTextNode,
addEventListener: chain( addEventListener ), addEventListener: chain( addEventListener ),
removeEventListener: chain( removeEventListener ), removeEventListener: chain( removeEventListener ),

View file

@ -2,7 +2,8 @@
( function () { ( function () {
/*global Node, Text, Element, HTMLDocument, window, document */ /*global Node, Text, Element, HTMLDocument, window, document, navigator,
setTimeout, editor */
"use strict"; "use strict";
@ -56,6 +57,8 @@ var isBlock = function ( el ) {
return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP; return el.isBlock() ? FILTER_ACCEPT : FILTER_SKIP;
}; };
var useTextFixer = !!( window.opera || window.ie ); var useTextFixer = !!( window.opera || window.ie );
var cantFocusEmptyTextNodes =
/WebKit/.test( navigator.userAgent ) || !!window.ie;
implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], { implement( window.Node ? [ Node ] : [ Text, Element, HTMLDocument ], {
isInline: $False, isInline: $False,
@ -376,7 +379,14 @@ implement([ Element ], {
if ( el.isInline() ) { if ( el.isInline() ) {
if ( !el.firstChild ) { if ( !el.firstChild ) {
fixer = doc.createTextNode( /* isWebkit ? '\u200B' :*/ '' ); if ( cantFocusEmptyTextNodes ) {
fixer = doc.createTextNode( '\u200B' );
setTimeout( function () {
editor._setPlaceholderTextNode( fixer );
}, 0 );
} else {
fixer = doc.createTextNode( '' );
}
} }
} else { } else {
if ( useTextFixer ) { if ( useTextFixer ) {
@ -415,7 +425,7 @@ implement([ Element ], {
} }
}); });
// Fix IE9's buggy implementation of Text#splitText. // Fix IE8/9's buggy implementation of Text#splitText.
// If the split is at the end of the node, it doesn't insert the newly split // If the split is at the end of the node, it doesn't insert the newly split
// node into the document, and sets its value to undefined rather than ''. // node into the document, and sets its value to undefined rather than ''.
// And even if the split is not at the end, the original node is removed from // And even if the split is not at the end, the original node is removed from