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

Allowing arbitrary nesting for bullets

This allows any level of indentation for bullets. It does this by introducing
invalid HTML. In particular, you can make bullets like:

<ul>
  <ul>
    <li>foo</li>
  </ul>
</ul>
This commit is contained in:
Islam Sharabash 2016-06-28 21:31:31 -07:00 committed by Neil Jenkins
parent 4f83b0a8bc
commit 6a9582c7e7
5 changed files with 139 additions and 48 deletions

View file

@ -1636,12 +1636,9 @@ var keyHandlers = {
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 we find a UL or OL (so are in a list, node must be an LI)
if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) { if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) {
// AND the LI is not the first in the list
if ( node.previousSibling ) {
// Then increase the list level // Then increase the list level
event.preventDefault(); event.preventDefault();
self.modifyBlocks( increaseListLevel, range ); self.modifyBlocks( increaseListLevel, range );
}
break; break;
} }
node = parent; node = parent;
@ -3764,19 +3761,19 @@ var makeOrderedList = function ( frag ) {
var removeList = function ( frag ) { var removeList = function ( frag ) {
var lists = frag.querySelectorAll( 'UL, OL' ), var lists = frag.querySelectorAll( 'UL, OL' ),
i, l, ll, list, listFrag, children, child; items = frag.querySelectorAll( 'LI' ),
i, l, list, listFrag, item;
for ( i = 0, l = lists.length; i < l; i += 1 ) { for ( i = 0, l = lists.length; i < l; i += 1 ) {
list = lists[i]; list = lists[i];
listFrag = empty( list ); listFrag = empty( list );
children = listFrag.childNodes;
ll = children.length;
while ( ll-- ) {
child = children[ll];
replaceWith( child, empty( child ) );
}
fixContainer( listFrag, this._root ); fixContainer( listFrag, this._root );
replaceWith( list, listFrag ); replaceWith( list, listFrag );
} }
for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i];
replaceWith( item, empty( item ) );
}
return frag; return frag;
}; };
@ -3785,7 +3782,6 @@ var increaseListLevel = function ( frag ) {
i, l, item, i, l, item,
type, newParent, type, newParent,
tagAttributes = this._config.tagAttributes, tagAttributes = this._config.tagAttributes,
listItemAttrs = tagAttributes.li,
listAttrs; listAttrs;
for ( i = 0, l = items.length; i < l; i += 1 ) { for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i]; item = items[i];
@ -3796,11 +3792,11 @@ var increaseListLevel = function ( frag ) {
if ( !newParent || !( newParent = newParent.lastChild ) || if ( !newParent || !( newParent = newParent.lastChild ) ||
newParent.nodeName !== type ) { newParent.nodeName !== type ) {
listAttrs = tagAttributes[ type.toLowerCase() ]; listAttrs = tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
replaceWith( replaceWith(
item, item,
this.createElement( 'LI', listItemAttrs, [ newParent
newParent = this.createElement( type, listAttrs )
])
); );
} }
newParent.appendChild( item ); newParent.appendChild( item );
@ -3823,6 +3819,15 @@ var decreaseListLevel = function ( frag ) {
if ( item.previousSibling ) { if ( item.previousSibling ) {
parent = split( parent, item, newParent, root ); parent = split( parent, item, newParent, root );
} }
// 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 );
}
} else {
while ( node ) { while ( node ) {
next = node.nextSibling; next = node.nextSibling;
if ( isContainer( node ) ) { if ( isContainer( node ) ) {
@ -3831,6 +3836,7 @@ var decreaseListLevel = function ( frag ) {
newParent.insertBefore( node, parent ); newParent.insertBefore( node, parent );
node = next; node = next;
} }
}
if ( newParent.nodeName === 'LI' && first.previousSibling ) { if ( newParent.nodeName === 'LI' && first.previousSibling ) {
split( newParent, first, newParent.parentNode, root ); split( newParent, first, newParent.parentNode, root );
} }

File diff suppressed because one or more lines are too long

View file

@ -1406,19 +1406,19 @@ var makeOrderedList = function ( frag ) {
var removeList = function ( frag ) { var removeList = function ( frag ) {
var lists = frag.querySelectorAll( 'UL, OL' ), var lists = frag.querySelectorAll( 'UL, OL' ),
i, l, ll, list, listFrag, children, child; items = frag.querySelectorAll( 'LI' ),
i, l, list, listFrag, item;
for ( i = 0, l = lists.length; i < l; i += 1 ) { for ( i = 0, l = lists.length; i < l; i += 1 ) {
list = lists[i]; list = lists[i];
listFrag = empty( list ); listFrag = empty( list );
children = listFrag.childNodes;
ll = children.length;
while ( ll-- ) {
child = children[ll];
replaceWith( child, empty( child ) );
}
fixContainer( listFrag, this._root ); fixContainer( listFrag, this._root );
replaceWith( list, listFrag ); replaceWith( list, listFrag );
} }
for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i];
replaceWith( item, empty( item ) );
}
return frag; return frag;
}; };
@ -1427,7 +1427,6 @@ var increaseListLevel = function ( frag ) {
i, l, item, i, l, item,
type, newParent, type, newParent,
tagAttributes = this._config.tagAttributes, tagAttributes = this._config.tagAttributes,
listItemAttrs = tagAttributes.li,
listAttrs; listAttrs;
for ( i = 0, l = items.length; i < l; i += 1 ) { for ( i = 0, l = items.length; i < l; i += 1 ) {
item = items[i]; item = items[i];
@ -1438,11 +1437,11 @@ var increaseListLevel = function ( frag ) {
if ( !newParent || !( newParent = newParent.lastChild ) || if ( !newParent || !( newParent = newParent.lastChild ) ||
newParent.nodeName !== type ) { newParent.nodeName !== type ) {
listAttrs = tagAttributes[ type.toLowerCase() ]; listAttrs = tagAttributes[ type.toLowerCase() ];
newParent = this.createElement( type, listAttrs );
replaceWith( replaceWith(
item, item,
this.createElement( 'LI', listItemAttrs, [ newParent
newParent = this.createElement( type, listAttrs )
])
); );
} }
newParent.appendChild( item ); newParent.appendChild( item );
@ -1465,6 +1464,15 @@ var decreaseListLevel = function ( frag ) {
if ( item.previousSibling ) { if ( item.previousSibling ) {
parent = split( parent, item, newParent, root ); parent = split( parent, item, newParent, root );
} }
// 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 );
}
} else {
while ( node ) { while ( node ) {
next = node.nextSibling; next = node.nextSibling;
if ( isContainer( node ) ) { if ( isContainer( node ) ) {
@ -1473,6 +1481,7 @@ var decreaseListLevel = function ( frag ) {
newParent.insertBefore( node, parent ); newParent.insertBefore( node, parent );
node = next; node = next;
} }
}
if ( newParent.nodeName === 'LI' && first.previousSibling ) { if ( newParent.nodeName === 'LI' && first.previousSibling ) {
split( newParent, first, newParent.parentNode, root ); split( newParent, first, newParent.parentNode, root );
} }

View file

@ -390,12 +390,9 @@ var keyHandlers = {
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 we find a UL or OL (so are in a list, node must be an LI)
if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) { if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) {
// AND the LI is not the first in the list
if ( node.previousSibling ) {
// Then increase the list level // Then increase the list level
event.preventDefault(); event.preventDefault();
self.modifyBlocks( increaseListLevel, range ); self.modifyBlocks( increaseListLevel, range );
}
break; break;
} }
node = parent; node = parent;

View file

@ -272,6 +272,85 @@ describe('Squire RTE', function () {
}); });
}); });
describe('multi-level lists', function () {
it('increases list indentation', function() {
var startHTML = '<ul><li><div>a</div></li><li><div>b</div></li><li><div>c</div></li></ul>';
editor.setHTML(startHTML);
expect(editor, 'to contain HTML', startHTML);
var range = doc.createRange();
var textNode = doc.getElementsByTagName('li').item(1).childNodes[0].childNodes[0]
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
editor.setSelection(range);
editor.increaseListLevel()
expect(editor, 'to contain HTML', '<ul><li><div>a</div></li><ul><li><div>b</div></li></ul><li><div>c</div></li></ul>');
});
it('increases list indentation 2', function() {
var startHTML = '<ul><li><div>a</div></li><li><div>b</div></li><li><div>c</div></li></ul>';
editor.setHTML(startHTML);
expect(editor, 'to contain HTML', startHTML);
var range = doc.createRange();
var textNode = doc.getElementsByTagName('li').item(1).childNodes[0].childNodes[0]
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
editor.setSelection(range);
editor.increaseListLevel()
editor.increaseListLevel()
expect(editor, 'to contain HTML', '<ul><li><div>a</div></li><ul><ul><li><div>b</div></li></ul></ul><li><div>c</div></li></ul>');
});
it('decreases list indentation', function() {
var startHTML = '<ul><li><div>a</div></li><ul><li><div>b</div></li></ul><li><div>c</div></li></ul>';
editor.setHTML(startHTML);
expect(editor, 'to contain HTML', startHTML);
var range = doc.createRange();
var textNode = doc.getElementsByTagName('li').item(1).childNodes[0].childNodes[0]
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
editor.setSelection(range);
editor.decreaseListLevel()
expect(editor, 'to contain HTML', '<ul><li><div>a</div></li><li><div>b</div></li><li><div>c</div></li></ul>');
});
it('decreases list indentation 2', function() {
var startHTML = '<ul><li><div>a</div></li><ul><ul><li><div>b</div></li></ul></ul><li><div>c</div></li></ul>';
editor.setHTML(startHTML);
expect(editor, 'to contain HTML', startHTML);
var range = doc.createRange();
var textNode = doc.getElementsByTagName('li').item(1).childNodes[0].childNodes[0]
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
editor.setSelection(range);
editor.decreaseListLevel()
editor.decreaseListLevel()
expect(editor, 'to contain HTML', '<ul><li><div>a</div></li><li><div>b</div></li><li><div>c</div></li></ul>');
});
it('removes lists', function() {
var startHTML = '<ul><li><div>foo</div></li><ul><li><div>bar</div></li></ul></ul>';
editor.setHTML(startHTML);
expect(editor, 'to contain HTML', startHTML);
var range = doc.createRange();
var textNode = doc.getElementsByTagName('li').item(1).childNodes[0].childNodes[0]
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
editor.setSelection(range);
editor.removeList()
expect(editor, 'to contain HTML', '<ul><li><div>foo</div></li></ul><div>bar</div>');
})
});
afterEach(function () { afterEach(function () {
editor = null; editor = null;
var iframe = document.getElementById('testFrame'); var iframe = document.getElementById('testFrame');