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

Tidy. Make backspace/delete aware of non-editable blocks

This commit is contained in:
Neil Jenkins 2012-08-08 16:20:28 +10:00
parent f2090b05d0
commit 52a10d463c
2 changed files with 90 additions and 71 deletions

File diff suppressed because one or more lines are too long

View file

@ -15,19 +15,21 @@
FILTER_ACCEPT = 1, // NodeFilter.FILTER_ACCEPT, FILTER_ACCEPT = 1, // NodeFilter.FILTER_ACCEPT,
FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP; FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP;
var win = doc.defaultView, var win = doc.defaultView;
body = doc.body; var body = doc.body;
// Browser sniffing. Unfortunately necessary. // Browser sniffing. Unfortunately necessary.
var ua = navigator.userAgent;
var isOpera = !!win.opera; var isOpera = !!win.opera;
var isIE = !!win.ie; var isIE = !!win.ie;
var isIE8 = win.ie === 8; var isIE8 = ( win.ie === 8 );
var isGecko = /Gecko\//.test( navigator.userAgent ); var isGecko = /Gecko\//.test( ua );
var isWebKit = /WebKit/.test( navigator.userAgent ); var isWebKit = /WebKit/.test( ua );
var isIOS = /iP(?:ad|hone|od)/.test( navigator.userAgent ); var isIOS = /iP(?:ad|hone|od)/.test( ua );
var useTextFixer = isIE || isOpera; var useTextFixer = isIE || isOpera;
var cantFocusEmptyTextNodes = isIE || isWebKit; var cantFocusEmptyTextNodes = isIE || isWebKit;
var losesSelectionOnBlur = isIE;
// --- DOM Sugar --- // --- DOM Sugar ---
@ -135,6 +137,7 @@
}; };
var sel = win.getSelection(); var sel = win.getSelection();
var lastSelection = null;
var setSelection = function ( range ) { var setSelection = function ( range ) {
if ( range ) { if ( range ) {
@ -150,7 +153,6 @@
} }
}; };
var lastSelection = null;
var getSelection = function () { var getSelection = function () {
if ( sel.rangeCount ) { if ( sel.rangeCount ) {
lastSelection = sel.getRangeAt( 0 ).cloneRange(); lastSelection = sel.getRangeAt( 0 ).cloneRange();
@ -167,14 +169,10 @@
// IE9 loses selection state of iframe on blur, so make sure we // IE9 loses selection state of iframe on blur, so make sure we
// cache it just before it loses focus. // cache it just before it loses focus.
if ( win.ie ) { if ( losesSelectionOnBlur ) {
win.addEventListener( 'beforedeactivate', getSelection, true ); win.addEventListener( 'beforedeactivate', getSelection, true );
} }
var lastAnchorNode;
var lastFocusNode;
var path = '';
// --- Workaround for browsers that can't focus empty text nodes --- // --- Workaround for browsers that can't focus empty text nodes ---
// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256 // WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256
@ -222,6 +220,10 @@
// --- Path change events --- // --- Path change events ---
var lastAnchorNode;
var lastFocusNode;
var path = '';
var updatePath = function ( range, force ) { var updatePath = function ( range, force ) {
if ( placeholderTextNode && !force ) { if ( placeholderTextNode && !force ) {
removePlaceholderTextNode( range ); removePlaceholderTextNode( range );
@ -292,6 +294,8 @@
// --- Bookmarking --- // --- Bookmarking ---
var indexOf = Array.prototype.indexOf;
var startSelectionId = 'ss-' + Date.now() + '-' + Math.random(); var startSelectionId = 'ss-' + Date.now() + '-' + Math.random();
var endSelectionId = 'es-' + Date.now() + '-' + Math.random(); var endSelectionId = 'es-' + Date.now() + '-' + Math.random();
@ -324,8 +328,6 @@
range.setEndBefore( endNode ); range.setEndBefore( endNode );
}; };
var indexOf = Array.prototype.indexOf;
var getRangeAndRemoveBookmark = function ( range ) { var getRangeAndRemoveBookmark = function ( range ) {
var start = doc.getElementById( startSelectionId ), var start = doc.getElementById( startSelectionId ),
end = doc.getElementById( endSelectionId ); end = doc.getElementById( endSelectionId );
@ -374,11 +376,11 @@
// These values are initialised in the editor.setHTML method, // These values are initialised in the editor.setHTML method,
// which is always called on initialisation. // which is always called on initialisation.
var undoIndex, // = -1, var undoIndex; // = -1,
undoStack, // = [], var undoStack; // = [],
undoStackLength, // = 0, var undoStackLength; // = 0,
isInUndoState, // = false, var isInUndoState; // = false,
docWasChanged = function () { var docWasChanged = function () {
if ( isInUndoState ) { if ( isInUndoState ) {
isInUndoState = false; isInUndoState = false;
fireEvent( 'undoStateChange', { fireEvent( 'undoStateChange', {
@ -398,16 +400,6 @@
if ( !event.ctrlKey && !event.metaKey && !event.altKey && if ( !event.ctrlKey && !event.metaKey && !event.altKey &&
( code < 16 || code > 20 ) && ( code < 16 || code > 20 ) &&
( code < 33 || code > 45 ) ) { ( code < 33 || code > 45 ) ) {
// If you select all in IE8 then type, it makes a P; replace it with
// a DIV.
var firstChild = body.firstChild;
if ( win.ie === 8 && firstChild.nodeName === 'P' ) {
saveRangeToBookmark( getSelection() );
firstChild.replaceWith( createElement( 'DIV', [
firstChild.empty()
]) );
setSelection( getRangeAndRemoveBookmark() );
}
docWasChanged(); docWasChanged();
} }
}); });
@ -727,6 +719,37 @@
// --- Block formatting --- // --- Block formatting ---
var tagAfterSplit = {
DIV: 'DIV',
PRE: 'DIV',
H1: 'DIV',
H2: 'DIV',
H3: 'DIV',
H4: 'DIV',
H5: 'DIV',
H6: 'DIV',
P: 'DIV',
DT: 'DD',
DD: 'DT',
LI: 'LI'
};
var splitBlock = function ( block, node, offset ) {
var splitTag = tagAfterSplit[ block.nodeName ],
nodeAfterSplit = node.split( offset, block.parentNode );
// Make sure the new node is the correct type.
if ( nodeAfterSplit.nodeName !== splitTag ) {
block = createElement( splitTag );
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
block.replaces( nodeAfterSplit )
.appendChild( nodeAfterSplit.empty() );
nodeAfterSplit = block;
}
return nodeAfterSplit;
};
var forEachBlock = function ( fn, mutates, range ) { var forEachBlock = function ( fn, mutates, range ) {
if ( !range && !( range = getSelection() ) ) { if ( !range && !( range = getSelection() ) ) {
return; return;
@ -912,37 +935,6 @@
return frag; return frag;
}; };
var tagAfterSplit = {
DIV: 'DIV',
PRE: 'DIV',
H1: 'DIV',
H2: 'DIV',
H3: 'DIV',
H4: 'DIV',
H5: 'DIV',
H6: 'DIV',
P: 'DIV',
DT: 'DD',
DD: 'DT',
LI: 'LI'
};
var splitBlock = function ( block, node, offset ) {
var splitTag = tagAfterSplit[ block.nodeName ],
nodeAfterSplit = node.split( offset, block.parentNode );
// Make sure the new node is the correct type.
if ( nodeAfterSplit.nodeName !== splitTag ) {
block = createElement( splitTag );
block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : '';
block.dir = nodeAfterSplit.dir;
block.replaces( nodeAfterSplit )
.appendChild( nodeAfterSplit.empty() );
nodeAfterSplit = block;
}
return nodeAfterSplit;
};
// --- Clean --- // --- Clean ---
var linkRegExp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])|(?:[\w\-.%+]+@(?:[\w\-]+\.)+[A-Z]{2,4}))/i; var linkRegExp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])|(?:[\w\-.%+]+@(?:[\w\-]+\.)+[A-Z]{2,4}))/i;
@ -1363,6 +1355,21 @@
} }
}; };
// If you select all in IE8 then type, it makes a P; replace it with
// a DIV.
if ( isIE8 ) {
addEventListener( 'keyup', function ( event ) {
var firstChild = body.firstChild;
if ( firstChild.nodeName === 'P' ) {
saveRangeToBookmark( getSelection() );
firstChild.replaceWith( createElement( 'DIV', [
firstChild.empty()
]) );
setSelection( getRangeAndRemoveBookmark() );
}
});
}
var keyHandlers = { var keyHandlers = {
enter: function ( event ) { enter: function ( event ) {
// We handle this ourselves // We handle this ourselves
@ -1454,7 +1461,7 @@
// Focus cursor // Focus cursor
// If there's a <b>/<i> etc. at the beginning of the split // If there's a <b>/<i> etc. at the beginning of the split
// make sure we focus inside it. // make sure we focus inside it.
while ( nodeAfterSplit.nodeType === ELEMENT_NODE) { while ( nodeAfterSplit.nodeType === ELEMENT_NODE ) {
var child = nodeAfterSplit.firstChild, var child = nodeAfterSplit.firstChild,
next; next;
@ -1517,6 +1524,12 @@
previous = current.getPreviousBlock(); previous = current.getPreviousBlock();
// Must not be at the very beginning of the text area. // Must not be at the very beginning of the text area.
if ( previous ) { if ( previous ) {
// If not editable, just delete whole block.
if ( !previous.isContentEditable ) {
previous.detach();
return;
}
// Otherwise merge.
previous.mergeWithBlock( current, range ); previous.mergeWithBlock( current, range );
// If deleted line between containers, merge newly adjacent // If deleted line between containers, merge newly adjacent
// containers. // containers.
@ -1566,6 +1579,12 @@
next = current.getNextBlock(); next = current.getNextBlock();
// Must not be at the very end of the text area. // Must not be at the very end of the text area.
if ( next ) { if ( next ) {
// If not editable, just delete whole block.
if ( !next.isContentEditable ) {
next.detach();
return;
}
// Otherwise merge.
current.mergeWithBlock( next, range ); current.mergeWithBlock( next, range );
// If deleted line between containers, merge newly adjacent // If deleted line between containers, merge newly adjacent
// containers. // containers.