0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2025-01-03 05:00:13 -05:00

New (increase|decrease)ListLevel algorithms

Fixes #287
This commit is contained in:
Neil Jenkins 2017-08-15 11:11:48 +10:00
parent 6842cb94eb
commit 833d7dfdbd
4 changed files with 298 additions and 148 deletions

View file

@ -1451,7 +1451,7 @@ var keyHandlers = {
// Break list
if ( getNearest( block, root, 'UL' ) ||
getNearest( block, root, 'OL' ) ) {
return self.modifyBlocks( decreaseListLevel, range );
return self.decreaseListLevel( range );
}
// Break blockquote
else if ( getNearest( block, root, 'BLOCKQUOTE' ) ) {
@ -1558,7 +1558,7 @@ var keyHandlers = {
// Break list
if ( getNearest( current, root, 'UL' ) ||
getNearest( current, root, 'OL' ) ) {
return self.modifyBlocks( decreaseListLevel, range );
return self.decreaseListLevel( range );
}
// Break blockquote
else if ( getNearest( current, root, 'BLOCKQUOTE' ) ) {
@ -1653,12 +1653,12 @@ var keyHandlers = {
if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) {
node = getStartBlockOfRange( range, root );
// Iterate through the block's parents
while ( parent = node.parentNode ) {
while ( ( parent = node.parentNode ) ) {
// If we find a UL or OL (so are in a list, node must be an LI)
if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) {
// Then increase the list level
event.preventDefault();
self.modifyBlocks( increaseListLevel, range );
self.increaseListLevel( range );
break;
}
node = parent;
@ -1676,7 +1676,7 @@ var keyHandlers = {
if ( getNearest( node, root, 'UL' ) ||
getNearest( node, root, 'OL' ) ) {
event.preventDefault();
self.modifyBlocks( decreaseListLevel, range );
self.decreaseListLevel( range );
}
}
},
@ -3888,77 +3888,155 @@ var removeList = function ( frag ) {
return frag;
};
var increaseListLevel = function ( frag ) {
var items = frag.querySelectorAll( 'LI' ),
i, l, item,
type, newParent,
tagAttributes = this._config.tagAttributes,
listAttrs;
for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i];
if ( !isContainer( item.firstChild ) ) {
// type => 'UL' or 'OL'
type = item.parentNode.nodeName;
newParent = item.previousSibling;
if ( !newParent || !( newParent = newParent.lastChild ) ||
newParent.nodeName !== type ) {
listAttrs = tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
replaceWith(
item,
newParent
);
var getListSelection = function ( range, root ) {
// Get start+end li in single common ancestor
var list = range.commonAncestorContainer;
var startLi = range.startContainer;
var endLi = range.endContainer;
while ( list && list !== root && !/^[OU]L$/.test( list.nodeName ) ) {
list = list.parentNode;
}
newParent.appendChild( item );
if ( !list || list === root ) {
return null;
}
if ( startLi === list ) {
startLi = startLi.childNodes[ range.startOffset ];
}
return frag;
if ( endLi === list ) {
endLi = endLi.childNodes[ range.endOffset ];
}
while ( startLi && startLi.parentNode !== list ) {
startLi = startLi.parentNode;
}
while ( endLi && endLi.parentNode !== list ) {
endLi = endLi.parentNode;
}
return [ list, startLi, endLi ];
};
var decreaseListLevel = function ( frag ) {
var root = this._root;
var items = frag.querySelectorAll( 'LI' );
Array.prototype.filter.call( items, function ( el ) {
return !isContainer( el.firstChild );
}).forEach( function ( item ) {
var parent = item.parentNode,
newParent = parent.parentNode,
first = item.firstChild,
node = first,
next;
if ( item.previousSibling ) {
parent = split( parent, item, newParent, root );
proto.increaseListLevel = function ( range ) {
if ( !range && !( range = this.getSelection() ) ) {
return this.focus();
}
// if the new parent is another list then we simply move the node
// e.g. `ul > ul > li` becomes `ul > li`
if ( /^[OU]L$/.test( newParent.nodeName ) ) {
newParent.insertBefore( item, parent );
if ( !parent.firstChild ) {
newParent.removeChild( parent );
var listSelection = getListSelection( range, root );
if ( !listSelection ) {
return this.focus();
}
} else {
while ( node ) {
next = node.nextSibling;
if ( isContainer( node ) ) {
break;
var list = listSelection[0];
var startLi = listSelection[1];
var endLi = listSelection[2];
if ( !startLi || startLi === list.firstChild ) {
return this.focus();
}
newParent.insertBefore( node, parent );
node = next;
// Save undo checkpoint and bookmark selection
this._recordUndoState( range, this._isInUndoState );
// Increase list depth
var type = list.nodeName;
var newParent = startLi.previousSibling;
var listAttrs, next;
if ( newParent.nodeName !== type ) {
listAttrs = this._config.tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
list.insertBefore( newParent, startLi );
}
do {
next = startLi === endLi ? null : startLi.nextSibling;
newParent.appendChild( startLi );
} while ( ( startLi = next ) );
next = newParent.nextSibling;
if ( next ) {
mergeContainers( next, this._root );
}
if ( newParent.nodeName === 'LI' && first.previousSibling ) {
split( newParent, first, newParent.parentNode, root );
// Restore selection
this._getRangeAndRemoveBookmark( range );
this.setSelection( range );
this._updatePath( range, true );
// We're not still in an undo state
if ( !canObserveMutations ) {
this._docWasChanged();
}
while ( item !== frag && !item.childNodes.length ) {
parent = item.parentNode;
parent.removeChild( item );
item = parent;
return this.focus();
};
proto.decreaseListLevel = function ( range ) {
if ( !range && !( range = this.getSelection() ) ) {
return this.focus();
}
}, this );
fixContainer( frag, root );
return frag;
var root = this._root;
var listSelection = getListSelection( range, root );
if ( !listSelection ) {
return this.focus();
}
var list = listSelection[0];
var startLi = listSelection[1];
var endLi = listSelection[2];
if ( !startLi ) {
startLi = list.firstChild;
}
if ( !endLi ) {
endLi = list.lastChild;
}
// Save undo checkpoint and bookmark selection
this._recordUndoState( range, this._isInUndoState );
// Find the new parent list node
var newParent = list.parentNode;
var next;
// Split list if necesary
var insertBefore = !endLi.nextSibling ?
list.nextSibling :
split( list, endLi.nextSibling, newParent, root );
if ( newParent !== root && newParent.nodeName === 'LI' ) {
newParent = newParent.parentNode;
while ( insertBefore ) {
next = insertBefore.nextSibling;
endLi.appendChild( insertBefore );
insertBefore = next;
}
insertBefore = list.parentNode.nextSibling;
}
var makeNotList = !/^[OU]L$/.test( newParent.nodeName );
do {
next = startLi === endLi ? null : startLi.nextSibling;
list.removeChild( startLi );
if ( makeNotList && startLi.nodeName === 'LI' ) {
startLi = this.createDefaultBlock([ empty( startLi ) ]);
}
newParent.insertBefore( startLi, insertBefore );
} while ( ( startLi = next ) );
if ( !list.firstChild ) {
detach( list );
}
if ( insertBefore ) {
mergeContainers( insertBefore, root );
}
// Restore selection
this._getRangeAndRemoveBookmark( range );
this.setSelection( range );
this._updatePath( range, true );
// We're not still in an undo state
if ( !canObserveMutations ) {
this._docWasChanged();
}
return this.focus();
};
proto._ensureBottomLine = function () {
@ -4554,9 +4632,6 @@ proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
proto.removeList = command( 'modifyBlocks', removeList );
proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel );
proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel );
// Node.js exports
Squire.isInline = isInline;
Squire.isBlock = isBlock;

File diff suppressed because one or more lines are too long

View file

@ -1454,77 +1454,155 @@ var removeList = function ( frag ) {
return frag;
};
var increaseListLevel = function ( frag ) {
var items = frag.querySelectorAll( 'LI' ),
i, l, item,
type, newParent,
tagAttributes = this._config.tagAttributes,
listAttrs;
for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i];
if ( !isContainer( item.firstChild ) ) {
// type => 'UL' or 'OL'
type = item.parentNode.nodeName;
newParent = item.previousSibling;
if ( !newParent || !( newParent = newParent.lastChild ) ||
newParent.nodeName !== type ) {
listAttrs = tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
replaceWith(
item,
newParent
);
var getListSelection = function ( range, root ) {
// Get start+end li in single common ancestor
var list = range.commonAncestorContainer;
var startLi = range.startContainer;
var endLi = range.endContainer;
while ( list && list !== root && !/^[OU]L$/.test( list.nodeName ) ) {
list = list.parentNode;
}
newParent.appendChild( item );
if ( !list || list === root ) {
return null;
}
if ( startLi === list ) {
startLi = startLi.childNodes[ range.startOffset ];
}
return frag;
if ( endLi === list ) {
endLi = endLi.childNodes[ range.endOffset ];
}
while ( startLi && startLi.parentNode !== list ) {
startLi = startLi.parentNode;
}
while ( endLi && endLi.parentNode !== list ) {
endLi = endLi.parentNode;
}
return [ list, startLi, endLi ];
};
var decreaseListLevel = function ( frag ) {
var root = this._root;
var items = frag.querySelectorAll( 'LI' );
Array.prototype.filter.call( items, function ( el ) {
return !isContainer( el.firstChild );
}).forEach( function ( item ) {
var parent = item.parentNode,
newParent = parent.parentNode,
first = item.firstChild,
node = first,
next;
if ( item.previousSibling ) {
parent = split( parent, item, newParent, root );
proto.increaseListLevel = function ( range ) {
if ( !range && !( range = this.getSelection() ) ) {
return this.focus();
}
// if the new parent is another list then we simply move the node
// e.g. `ul > ul > li` becomes `ul > li`
if ( /^[OU]L$/.test( newParent.nodeName ) ) {
newParent.insertBefore( item, parent );
if ( !parent.firstChild ) {
newParent.removeChild( parent );
var listSelection = getListSelection( range, root );
if ( !listSelection ) {
return this.focus();
}
} else {
while ( node ) {
next = node.nextSibling;
if ( isContainer( node ) ) {
break;
var list = listSelection[0];
var startLi = listSelection[1];
var endLi = listSelection[2];
if ( !startLi || startLi === list.firstChild ) {
return this.focus();
}
newParent.insertBefore( node, parent );
node = next;
// Save undo checkpoint and bookmark selection
this._recordUndoState( range, this._isInUndoState );
// Increase list depth
var type = list.nodeName;
var newParent = startLi.previousSibling;
var listAttrs, next;
if ( newParent.nodeName !== type ) {
listAttrs = this._config.tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
list.insertBefore( newParent, startLi );
}
do {
next = startLi === endLi ? null : startLi.nextSibling;
newParent.appendChild( startLi );
} while ( ( startLi = next ) );
next = newParent.nextSibling;
if ( next ) {
mergeContainers( next, this._root );
}
if ( newParent.nodeName === 'LI' && first.previousSibling ) {
split( newParent, first, newParent.parentNode, root );
// Restore selection
this._getRangeAndRemoveBookmark( range );
this.setSelection( range );
this._updatePath( range, true );
// We're not still in an undo state
if ( !canObserveMutations ) {
this._docWasChanged();
}
while ( item !== frag && !item.childNodes.length ) {
parent = item.parentNode;
parent.removeChild( item );
item = parent;
return this.focus();
};
proto.decreaseListLevel = function ( range ) {
if ( !range && !( range = this.getSelection() ) ) {
return this.focus();
}
}, this );
fixContainer( frag, root );
return frag;
var root = this._root;
var listSelection = getListSelection( range, root );
if ( !listSelection ) {
return this.focus();
}
var list = listSelection[0];
var startLi = listSelection[1];
var endLi = listSelection[2];
if ( !startLi ) {
startLi = list.firstChild;
}
if ( !endLi ) {
endLi = list.lastChild;
}
// Save undo checkpoint and bookmark selection
this._recordUndoState( range, this._isInUndoState );
// Find the new parent list node
var newParent = list.parentNode;
var next;
// Split list if necesary
var insertBefore = !endLi.nextSibling ?
list.nextSibling :
split( list, endLi.nextSibling, newParent, root );
if ( newParent !== root && newParent.nodeName === 'LI' ) {
newParent = newParent.parentNode;
while ( insertBefore ) {
next = insertBefore.nextSibling;
endLi.appendChild( insertBefore );
insertBefore = next;
}
insertBefore = list.parentNode.nextSibling;
}
var makeNotList = !/^[OU]L$/.test( newParent.nodeName );
do {
next = startLi === endLi ? null : startLi.nextSibling;
list.removeChild( startLi );
if ( makeNotList && startLi.nodeName === 'LI' ) {
startLi = this.createDefaultBlock([ empty( startLi ) ]);
}
newParent.insertBefore( startLi, insertBefore );
} while ( ( startLi = next ) );
if ( !list.firstChild ) {
detach( list );
}
if ( insertBefore ) {
mergeContainers( insertBefore, root );
}
// Restore selection
this._getRangeAndRemoveBookmark( range );
this.setSelection( range );
this._updatePath( range, true );
// We're not still in an undo state
if ( !canObserveMutations ) {
this._docWasChanged();
}
return this.focus();
};
proto._ensureBottomLine = function () {
@ -2119,6 +2197,3 @@ proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );
proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
proto.removeList = command( 'modifyBlocks', removeList );
proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel );
proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel );

View file

@ -192,7 +192,7 @@ var keyHandlers = {
// Break list
if ( getNearest( block, root, 'UL' ) ||
getNearest( block, root, 'OL' ) ) {
return self.modifyBlocks( decreaseListLevel, range );
return self.decreaseListLevel( range );
}
// Break blockquote
else if ( getNearest( block, root, 'BLOCKQUOTE' ) ) {
@ -299,7 +299,7 @@ var keyHandlers = {
// Break list
if ( getNearest( current, root, 'UL' ) ||
getNearest( current, root, 'OL' ) ) {
return self.modifyBlocks( decreaseListLevel, range );
return self.decreaseListLevel( range );
}
// Break blockquote
else if ( getNearest( current, root, 'BLOCKQUOTE' ) ) {
@ -394,12 +394,12 @@ var keyHandlers = {
if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) {
node = getStartBlockOfRange( range, root );
// Iterate through the block's parents
while ( parent = node.parentNode ) {
while ( ( parent = node.parentNode ) ) {
// If we find a UL or OL (so are in a list, node must be an LI)
if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) {
// Then increase the list level
event.preventDefault();
self.modifyBlocks( increaseListLevel, range );
self.increaseListLevel( range );
break;
}
node = parent;
@ -417,7 +417,7 @@ var keyHandlers = {
if ( getNearest( node, root, 'UL' ) ||
getNearest( node, root, 'OL' ) ) {
event.preventDefault();
self.modifyBlocks( decreaseListLevel, range );
self.decreaseListLevel( range );
}
}
},