mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-22 07:13:08 -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:
parent
4f83b0a8bc
commit
6a9582c7e7
5 changed files with 139 additions and 48 deletions
|
@ -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
|
// Then increase the list level
|
||||||
if ( node.previousSibling ) {
|
event.preventDefault();
|
||||||
// Then increase the list level
|
self.modifyBlocks( increaseListLevel, range );
|
||||||
event.preventDefault();
|
|
||||||
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,13 +3819,23 @@ var decreaseListLevel = function ( frag ) {
|
||||||
if ( item.previousSibling ) {
|
if ( item.previousSibling ) {
|
||||||
parent = split( parent, item, newParent, root );
|
parent = split( parent, item, newParent, root );
|
||||||
}
|
}
|
||||||
while ( node ) {
|
|
||||||
next = node.nextSibling;
|
// if the new parent is another list then we simply move the node
|
||||||
if ( isContainer( node ) ) {
|
// e.g. `ul > ul > li` becomes `ul > li`
|
||||||
break;
|
if ( /^[OU]L$/.test( newParent.nodeName ) ) {
|
||||||
|
newParent.insertBefore( item, parent );
|
||||||
|
if ( !parent.firstChild ) {
|
||||||
|
newParent.removeChild( parent );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while ( node ) {
|
||||||
|
next = node.nextSibling;
|
||||||
|
if ( isContainer( node ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newParent.insertBefore( node, parent );
|
||||||
|
node = next;
|
||||||
}
|
}
|
||||||
newParent.insertBefore( node, parent );
|
|
||||||
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
|
@ -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,13 +1464,23 @@ var decreaseListLevel = function ( frag ) {
|
||||||
if ( item.previousSibling ) {
|
if ( item.previousSibling ) {
|
||||||
parent = split( parent, item, newParent, root );
|
parent = split( parent, item, newParent, root );
|
||||||
}
|
}
|
||||||
while ( node ) {
|
|
||||||
next = node.nextSibling;
|
// if the new parent is another list then we simply move the node
|
||||||
if ( isContainer( node ) ) {
|
// e.g. `ul > ul > li` becomes `ul > li`
|
||||||
break;
|
if ( /^[OU]L$/.test( newParent.nodeName ) ) {
|
||||||
|
newParent.insertBefore( item, parent );
|
||||||
|
if ( !parent.firstChild ) {
|
||||||
|
newParent.removeChild( parent );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while ( node ) {
|
||||||
|
next = node.nextSibling;
|
||||||
|
if ( isContainer( node ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newParent.insertBefore( node, parent );
|
||||||
|
node = next;
|
||||||
}
|
}
|
||||||
newParent.insertBefore( node, parent );
|
|
||||||
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 );
|
||||||
|
|
|
@ -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
|
// Then increase the list level
|
||||||
if ( node.previousSibling ) {
|
event.preventDefault();
|
||||||
// Then increase the list level
|
self.modifyBlocks( increaseListLevel, range );
|
||||||
event.preventDefault();
|
|
||||||
self.modifyBlocks( increaseListLevel, range );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
node = parent;
|
node = parent;
|
||||||
|
|
|
@ -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');
|
||||||
|
|
Loading…
Reference in a new issue