mirror of
https://github.com/fastmail/Squire.git
synced 2024-12-31 11:54:03 -05:00
Add support mult-level lists.
* Hit tab to increase list depth, or call increaseListLevel method. * Hit enter on a blank item to decrease list depth, or call decreaseListLevel method.
This commit is contained in:
parent
bee49bef40
commit
6b754d423c
5 changed files with 320 additions and 138 deletions
|
@ -1,6 +1,6 @@
|
||||||
/* Copyright © 2011-2013 by Neil Jenkins. MIT Licensed. */
|
/* Copyright © 2011-2013 by Neil Jenkins. MIT Licensed. */
|
||||||
|
|
||||||
( function ( doc ) {
|
( function ( doc, undefined ) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
/*global doc, navigator */
|
/*global doc, navigator */
|
||||||
|
@ -506,26 +506,40 @@ function mergeWithBlock ( block, next, range ) {
|
||||||
|
|
||||||
function mergeContainers ( node ) {
|
function mergeContainers ( node ) {
|
||||||
var prev = node.previousSibling,
|
var prev = node.previousSibling,
|
||||||
first = node.firstChild;
|
first = node.firstChild,
|
||||||
|
isListItem = ( node.nodeName === 'LI' );
|
||||||
|
|
||||||
|
// Do not merge LIs, unless it only contains a UL
|
||||||
|
if ( isListItem && ( !first || !/^[OU]L$/.test( first.nodeName ) ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( prev && areAlike( prev, node ) && isContainer( prev ) ) {
|
if ( prev && areAlike( prev, node ) && isContainer( prev ) ) {
|
||||||
detach( node );
|
detach( node );
|
||||||
prev.appendChild( empty( node ) );
|
prev.appendChild( empty( node ) );
|
||||||
if ( first ) {
|
if ( first ) {
|
||||||
mergeContainers( first );
|
mergeContainers( first );
|
||||||
}
|
}
|
||||||
|
} else if ( isListItem ) {
|
||||||
|
prev = node.ownerDocument.createElement( 'div' );
|
||||||
|
node.insertBefore( prev, first );
|
||||||
|
fixCursor( prev );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElement ( doc, tag, props, children ) {
|
function createElement ( doc, tag, props, children ) {
|
||||||
var el = doc.createElement( tag ),
|
var el = doc.createElement( tag ),
|
||||||
attr, i, l;
|
attr, value, i, l;
|
||||||
if ( props instanceof Array ) {
|
if ( props instanceof Array ) {
|
||||||
children = props;
|
children = props;
|
||||||
props = null;
|
props = null;
|
||||||
}
|
}
|
||||||
if ( props ) {
|
if ( props ) {
|
||||||
for ( attr in props ) {
|
for ( attr in props ) {
|
||||||
el.setAttribute( attr, props[ attr ] );
|
value = props[ attr ];
|
||||||
|
if ( value !== undefined ) {
|
||||||
|
el.setAttribute( attr, props[ attr ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( children ) {
|
if ( children ) {
|
||||||
|
@ -1076,6 +1090,7 @@ var expandRangeToBlockBoundaries = function ( range ) {
|
||||||
isInline,
|
isInline,
|
||||||
isBlock,
|
isBlock,
|
||||||
isContainer,
|
isContainer,
|
||||||
|
getBlockWalker,
|
||||||
getPreviousBlock,
|
getPreviousBlock,
|
||||||
getNextBlock,
|
getNextBlock,
|
||||||
getNearest,
|
getNearest,
|
||||||
|
@ -2023,83 +2038,128 @@ var removeBlockQuote = function ( frag ) {
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeList = function ( self, nodes, type ) {
|
var makeList = function ( self, frag, type ) {
|
||||||
var i, l, node, tag, prev, replacement;
|
var walker = getBlockWalker( frag ),
|
||||||
for ( i = 0, l = nodes.length; i < l; i += 1 ) {
|
node, tag, prev, newLi;
|
||||||
node = nodes[i];
|
|
||||||
tag = node.nodeName;
|
while ( node = walker.nextNode() ) {
|
||||||
if ( isBlock( node ) ) {
|
tag = node.parentNode.nodeName;
|
||||||
if ( tag !== 'LI' ) {
|
if ( tag !== 'LI' ) {
|
||||||
replacement = self.createElement( 'LI', {
|
newLi = self.createElement( 'LI', {
|
||||||
'class': node.dir === 'rtl' ? 'dir-rtl' : '',
|
'class': node.dir === 'rtl' ? 'dir-rtl' : undefined,
|
||||||
dir: node.dir
|
dir: node.dir || undefined
|
||||||
}, [
|
});
|
||||||
empty( node )
|
// Have we replaced the previous block with a new <ul>/<ol>?
|
||||||
]);
|
if ( ( prev = node.previousSibling ) &&
|
||||||
if ( node.parentNode.nodeName === type ) {
|
prev.nodeName === type ) {
|
||||||
replaceWith( node, replacement );
|
prev.appendChild( newLi );
|
||||||
}
|
|
||||||
else if ( ( prev = node.previousSibling ) &&
|
|
||||||
prev.nodeName === type ) {
|
|
||||||
prev.appendChild( replacement );
|
|
||||||
detach( node );
|
|
||||||
i -= 1;
|
|
||||||
l -= 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
replaceWith(
|
|
||||||
node,
|
|
||||||
self.createElement( type, [
|
|
||||||
replacement
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if ( isContainer( node ) ) {
|
// Otherwise, replace this block with the <ul>/<ol>
|
||||||
if ( tag !== type && ( /^[DOU]L$/.test( tag ) ) ) {
|
else {
|
||||||
|
replaceWith(
|
||||||
|
node,
|
||||||
|
self.createElement( type, [
|
||||||
|
newLi
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newLi.appendChild( node );
|
||||||
|
} else {
|
||||||
|
node = node.parentNode.parentNode;
|
||||||
|
tag = node.nodeName;
|
||||||
|
if ( tag !== type && ( /^[OU]L$/.test( tag ) ) ) {
|
||||||
replaceWith( node,
|
replaceWith( node,
|
||||||
self.createElement( type, [ empty( node ) ] )
|
self.createElement( type, [ empty( node ) ] )
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
makeList( self, node.childNodes, type );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeUnorderedList = function ( frag ) {
|
var makeUnorderedList = function ( frag ) {
|
||||||
makeList( this, frag.childNodes, 'UL' );
|
makeList( this, frag, 'UL' );
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeOrderedList = function ( frag ) {
|
var makeOrderedList = function ( frag ) {
|
||||||
makeList( this, frag.childNodes, 'OL' );
|
makeList( this, frag, 'OL' );
|
||||||
|
return frag;
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeList = function ( frag ) {
|
||||||
|
var lists = frag.querySelectorAll( 'UL, OL' ),
|
||||||
|
i, l, ll, list, listFrag, children, child;
|
||||||
|
for ( i = 0, l = lists.length; i < l; i += 1 ) {
|
||||||
|
list = lists[i];
|
||||||
|
listFrag = empty( list );
|
||||||
|
children = listFrag.childNodes;
|
||||||
|
ll = children.length;
|
||||||
|
while ( ll-- ) {
|
||||||
|
child = children[ll];
|
||||||
|
replaceWith( child, empty( child ) );
|
||||||
|
}
|
||||||
|
wrapTopLevelInline( listFrag, 'DIV' );
|
||||||
|
replaceWith( list, listFrag );
|
||||||
|
}
|
||||||
|
return frag;
|
||||||
|
};
|
||||||
|
|
||||||
|
var increaseListLevel = function ( frag ) {
|
||||||
|
var items = frag.querySelectorAll( 'LI' ),
|
||||||
|
i, l, item,
|
||||||
|
type, newParent;
|
||||||
|
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 ) {
|
||||||
|
replaceWith(
|
||||||
|
item,
|
||||||
|
this.createElement( 'LI', [
|
||||||
|
newParent = this.createElement( type )
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newParent.appendChild( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var decreaseListLevel = function ( frag ) {
|
var decreaseListLevel = function ( frag ) {
|
||||||
var lists = frag.querySelectorAll( 'UL, OL' );
|
var items = frag.querySelectorAll( 'LI' );
|
||||||
Array.prototype.filter.call( lists, function ( el ) {
|
Array.prototype.filter.call( items, function ( el ) {
|
||||||
return !getNearest( el.parentNode, 'UL' ) &&
|
return !isContainer( el.firstChild );
|
||||||
!getNearest( el.parentNode, 'OL' );
|
}).forEach( function ( item ) {
|
||||||
}).forEach( function ( el ) {
|
var parent = item.parentNode,
|
||||||
var frag = empty( el ),
|
newParent = parent.parentNode,
|
||||||
children = frag.childNodes,
|
first = item.firstChild,
|
||||||
l = children.length,
|
node = first,
|
||||||
child;
|
next;
|
||||||
while ( l-- ) {
|
if ( item.previousSibling ) {
|
||||||
child = children[l];
|
parent = split( parent, item, newParent );
|
||||||
if ( child.nodeName === 'LI' ) {
|
}
|
||||||
frag.replaceChild( this.createElement( 'DIV', {
|
while ( node ) {
|
||||||
'class': child.dir === 'rtl' ? 'dir-rtl' : '',
|
next = node.nextSibling;
|
||||||
dir: child.dir
|
if ( isContainer( node ) ) {
|
||||||
}, [
|
break;
|
||||||
empty( child )
|
}
|
||||||
]), child );
|
newParent.insertBefore( node, parent );
|
||||||
}
|
node = next;
|
||||||
|
}
|
||||||
|
if ( newParent.nodeName === 'LI' && first.previousSibling ) {
|
||||||
|
split( newParent, first, newParent.parentNode );
|
||||||
|
}
|
||||||
|
while ( item !== frag && !item.childNodes.length ) {
|
||||||
|
parent = item.parentNode;
|
||||||
|
parent.removeChild( item );
|
||||||
|
item = parent;
|
||||||
}
|
}
|
||||||
replaceWith( el, frag );
|
|
||||||
}, this );
|
}, this );
|
||||||
|
wrapTopLevelInline( frag, 'DIV' );
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2689,7 +2749,8 @@ var keyHandlers = {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Must have some form of selection
|
// Must have some form of selection
|
||||||
var range = self.getSelection();
|
var range = self.getSelection(),
|
||||||
|
block, parent, tag, splitTag, nodeAfterSplit;
|
||||||
if ( !range ) { return; }
|
if ( !range ) { return; }
|
||||||
|
|
||||||
// Save undo checkpoint and add any links in the preceding section.
|
// Save undo checkpoint and add any links in the preceding section.
|
||||||
|
@ -2703,10 +2764,12 @@ var keyHandlers = {
|
||||||
deleteContentsOfRange( range );
|
deleteContentsOfRange( range );
|
||||||
}
|
}
|
||||||
|
|
||||||
var block = getStartBlockOfRange( range ),
|
block = getStartBlockOfRange( range );
|
||||||
tag = block ? block.nodeName : 'DIV',
|
if ( block && ( parent = getNearest( block, 'LI' ) ) ) {
|
||||||
splitTag = tagAfterSplit[ tag ],
|
block = parent;
|
||||||
nodeAfterSplit;
|
}
|
||||||
|
tag = block ? block.nodeName : 'DIV';
|
||||||
|
splitTag = tagAfterSplit[ tag ];
|
||||||
|
|
||||||
// If this is a malformed bit of document, just play it safe
|
// If this is a malformed bit of document, just play it safe
|
||||||
// and insert a <br>.
|
// and insert a <br>.
|
||||||
|
@ -2725,7 +2788,7 @@ var keyHandlers = {
|
||||||
replacement;
|
replacement;
|
||||||
if ( !splitTag ) {
|
if ( !splitTag ) {
|
||||||
// If the selection point is inside the block, we're going to
|
// If the selection point is inside the block, we're going to
|
||||||
// rewrite it so our saved referece points won't be valid.
|
// rewrite it so our saved reference points won't be valid.
|
||||||
// Pick a node at a deeper point in the tree to avoid this.
|
// Pick a node at a deeper point in the tree to avoid this.
|
||||||
if ( splitNode === block ) {
|
if ( splitNode === block ) {
|
||||||
splitNode = splitOffset ?
|
splitNode = splitOffset ?
|
||||||
|
@ -2943,6 +3006,31 @@ var keyHandlers = {
|
||||||
setTimeout( function () { afterDelete( self ); }, 0 );
|
setTimeout( function () { afterDelete( self ); }, 0 );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tab: function ( self, event ) {
|
||||||
|
var range = self.getSelection(),
|
||||||
|
node, parent;
|
||||||
|
// If no selection and in an empty block
|
||||||
|
if ( range.collapsed &&
|
||||||
|
rangeDoesStartAtBlockBoundary( range ) &&
|
||||||
|
rangeDoesEndAtBlockBoundary( range ) ) {
|
||||||
|
node = getStartBlockOfRange( range );
|
||||||
|
// Iterate through the block's parents
|
||||||
|
while ( parent = node.parentNode ) {
|
||||||
|
// If we find a UL or OL (so are in a list, node must be an LI)
|
||||||
|
if ( /^[OU]L$/.test( parent.nodeName ) ) {
|
||||||
|
// AND the LI is not the first in the list
|
||||||
|
if ( node.previousSibling ) {
|
||||||
|
// Then increase the list level
|
||||||
|
event.preventDefault();
|
||||||
|
self.modifyBlocks( increaseListLevel, range );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
space: function ( self ) {
|
space: function ( self ) {
|
||||||
var range = self.getSelection();
|
var range = self.getSelection();
|
||||||
self._recordUndoState( range );
|
self._recordUndoState( range );
|
||||||
|
@ -3312,7 +3400,10 @@ proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );
|
||||||
|
|
||||||
proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
|
proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
|
||||||
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
|
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
|
||||||
proto.removeList = command( 'modifyBlocks', decreaseListLevel );
|
proto.removeList = command( 'modifyBlocks', removeList );
|
||||||
|
|
||||||
|
proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel );
|
||||||
|
proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel );
|
||||||
/*global top, win, doc, Squire */
|
/*global top, win, doc, Squire */
|
||||||
|
|
||||||
if ( top !== win ) {
|
if ( top !== win ) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
205
source/Editor.js
205
source/Editor.js
|
@ -28,6 +28,7 @@
|
||||||
isInline,
|
isInline,
|
||||||
isBlock,
|
isBlock,
|
||||||
isContainer,
|
isContainer,
|
||||||
|
getBlockWalker,
|
||||||
getPreviousBlock,
|
getPreviousBlock,
|
||||||
getNextBlock,
|
getNextBlock,
|
||||||
getNearest,
|
getNearest,
|
||||||
|
@ -975,83 +976,128 @@ var removeBlockQuote = function ( frag ) {
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeList = function ( self, nodes, type ) {
|
var makeList = function ( self, frag, type ) {
|
||||||
var i, l, node, tag, prev, replacement;
|
var walker = getBlockWalker( frag ),
|
||||||
for ( i = 0, l = nodes.length; i < l; i += 1 ) {
|
node, tag, prev, newLi;
|
||||||
node = nodes[i];
|
|
||||||
tag = node.nodeName;
|
while ( node = walker.nextNode() ) {
|
||||||
if ( isBlock( node ) ) {
|
tag = node.parentNode.nodeName;
|
||||||
if ( tag !== 'LI' ) {
|
if ( tag !== 'LI' ) {
|
||||||
replacement = self.createElement( 'LI', {
|
newLi = self.createElement( 'LI', {
|
||||||
'class': node.dir === 'rtl' ? 'dir-rtl' : '',
|
'class': node.dir === 'rtl' ? 'dir-rtl' : undefined,
|
||||||
dir: node.dir
|
dir: node.dir || undefined
|
||||||
}, [
|
});
|
||||||
empty( node )
|
// Have we replaced the previous block with a new <ul>/<ol>?
|
||||||
]);
|
if ( ( prev = node.previousSibling ) &&
|
||||||
if ( node.parentNode.nodeName === type ) {
|
prev.nodeName === type ) {
|
||||||
replaceWith( node, replacement );
|
prev.appendChild( newLi );
|
||||||
}
|
|
||||||
else if ( ( prev = node.previousSibling ) &&
|
|
||||||
prev.nodeName === type ) {
|
|
||||||
prev.appendChild( replacement );
|
|
||||||
detach( node );
|
|
||||||
i -= 1;
|
|
||||||
l -= 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
replaceWith(
|
|
||||||
node,
|
|
||||||
self.createElement( type, [
|
|
||||||
replacement
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if ( isContainer( node ) ) {
|
// Otherwise, replace this block with the <ul>/<ol>
|
||||||
if ( tag !== type && ( /^[DOU]L$/.test( tag ) ) ) {
|
else {
|
||||||
|
replaceWith(
|
||||||
|
node,
|
||||||
|
self.createElement( type, [
|
||||||
|
newLi
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newLi.appendChild( node );
|
||||||
|
} else {
|
||||||
|
node = node.parentNode.parentNode;
|
||||||
|
tag = node.nodeName;
|
||||||
|
if ( tag !== type && ( /^[OU]L$/.test( tag ) ) ) {
|
||||||
replaceWith( node,
|
replaceWith( node,
|
||||||
self.createElement( type, [ empty( node ) ] )
|
self.createElement( type, [ empty( node ) ] )
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
makeList( self, node.childNodes, type );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeUnorderedList = function ( frag ) {
|
var makeUnorderedList = function ( frag ) {
|
||||||
makeList( this, frag.childNodes, 'UL' );
|
makeList( this, frag, 'UL' );
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeOrderedList = function ( frag ) {
|
var makeOrderedList = function ( frag ) {
|
||||||
makeList( this, frag.childNodes, 'OL' );
|
makeList( this, frag, 'OL' );
|
||||||
|
return frag;
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeList = function ( frag ) {
|
||||||
|
var lists = frag.querySelectorAll( 'UL, OL' ),
|
||||||
|
i, l, ll, list, listFrag, children, child;
|
||||||
|
for ( i = 0, l = lists.length; i < l; i += 1 ) {
|
||||||
|
list = lists[i];
|
||||||
|
listFrag = empty( list );
|
||||||
|
children = listFrag.childNodes;
|
||||||
|
ll = children.length;
|
||||||
|
while ( ll-- ) {
|
||||||
|
child = children[ll];
|
||||||
|
replaceWith( child, empty( child ) );
|
||||||
|
}
|
||||||
|
wrapTopLevelInline( listFrag, 'DIV' );
|
||||||
|
replaceWith( list, listFrag );
|
||||||
|
}
|
||||||
|
return frag;
|
||||||
|
};
|
||||||
|
|
||||||
|
var increaseListLevel = function ( frag ) {
|
||||||
|
var items = frag.querySelectorAll( 'LI' ),
|
||||||
|
i, l, item,
|
||||||
|
type, newParent;
|
||||||
|
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 ) {
|
||||||
|
replaceWith(
|
||||||
|
item,
|
||||||
|
this.createElement( 'LI', [
|
||||||
|
newParent = this.createElement( type )
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newParent.appendChild( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
var decreaseListLevel = function ( frag ) {
|
var decreaseListLevel = function ( frag ) {
|
||||||
var lists = frag.querySelectorAll( 'UL, OL' );
|
var items = frag.querySelectorAll( 'LI' );
|
||||||
Array.prototype.filter.call( lists, function ( el ) {
|
Array.prototype.filter.call( items, function ( el ) {
|
||||||
return !getNearest( el.parentNode, 'UL' ) &&
|
return !isContainer( el.firstChild );
|
||||||
!getNearest( el.parentNode, 'OL' );
|
}).forEach( function ( item ) {
|
||||||
}).forEach( function ( el ) {
|
var parent = item.parentNode,
|
||||||
var frag = empty( el ),
|
newParent = parent.parentNode,
|
||||||
children = frag.childNodes,
|
first = item.firstChild,
|
||||||
l = children.length,
|
node = first,
|
||||||
child;
|
next;
|
||||||
while ( l-- ) {
|
if ( item.previousSibling ) {
|
||||||
child = children[l];
|
parent = split( parent, item, newParent );
|
||||||
if ( child.nodeName === 'LI' ) {
|
}
|
||||||
frag.replaceChild( this.createElement( 'DIV', {
|
while ( node ) {
|
||||||
'class': child.dir === 'rtl' ? 'dir-rtl' : '',
|
next = node.nextSibling;
|
||||||
dir: child.dir
|
if ( isContainer( node ) ) {
|
||||||
}, [
|
break;
|
||||||
empty( child )
|
}
|
||||||
]), child );
|
newParent.insertBefore( node, parent );
|
||||||
}
|
node = next;
|
||||||
|
}
|
||||||
|
if ( newParent.nodeName === 'LI' && first.previousSibling ) {
|
||||||
|
split( newParent, first, newParent.parentNode );
|
||||||
|
}
|
||||||
|
while ( item !== frag && !item.childNodes.length ) {
|
||||||
|
parent = item.parentNode;
|
||||||
|
parent.removeChild( item );
|
||||||
|
item = parent;
|
||||||
}
|
}
|
||||||
replaceWith( el, frag );
|
|
||||||
}, this );
|
}, this );
|
||||||
|
wrapTopLevelInline( frag, 'DIV' );
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1641,7 +1687,8 @@ var keyHandlers = {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Must have some form of selection
|
// Must have some form of selection
|
||||||
var range = self.getSelection();
|
var range = self.getSelection(),
|
||||||
|
block, parent, tag, splitTag, nodeAfterSplit;
|
||||||
if ( !range ) { return; }
|
if ( !range ) { return; }
|
||||||
|
|
||||||
// Save undo checkpoint and add any links in the preceding section.
|
// Save undo checkpoint and add any links in the preceding section.
|
||||||
|
@ -1655,10 +1702,12 @@ var keyHandlers = {
|
||||||
deleteContentsOfRange( range );
|
deleteContentsOfRange( range );
|
||||||
}
|
}
|
||||||
|
|
||||||
var block = getStartBlockOfRange( range ),
|
block = getStartBlockOfRange( range );
|
||||||
tag = block ? block.nodeName : 'DIV',
|
if ( block && ( parent = getNearest( block, 'LI' ) ) ) {
|
||||||
splitTag = tagAfterSplit[ tag ],
|
block = parent;
|
||||||
nodeAfterSplit;
|
}
|
||||||
|
tag = block ? block.nodeName : 'DIV';
|
||||||
|
splitTag = tagAfterSplit[ tag ];
|
||||||
|
|
||||||
// If this is a malformed bit of document, just play it safe
|
// If this is a malformed bit of document, just play it safe
|
||||||
// and insert a <br>.
|
// and insert a <br>.
|
||||||
|
@ -1677,7 +1726,7 @@ var keyHandlers = {
|
||||||
replacement;
|
replacement;
|
||||||
if ( !splitTag ) {
|
if ( !splitTag ) {
|
||||||
// If the selection point is inside the block, we're going to
|
// If the selection point is inside the block, we're going to
|
||||||
// rewrite it so our saved referece points won't be valid.
|
// rewrite it so our saved reference points won't be valid.
|
||||||
// Pick a node at a deeper point in the tree to avoid this.
|
// Pick a node at a deeper point in the tree to avoid this.
|
||||||
if ( splitNode === block ) {
|
if ( splitNode === block ) {
|
||||||
splitNode = splitOffset ?
|
splitNode = splitOffset ?
|
||||||
|
@ -1895,6 +1944,31 @@ var keyHandlers = {
|
||||||
setTimeout( function () { afterDelete( self ); }, 0 );
|
setTimeout( function () { afterDelete( self ); }, 0 );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tab: function ( self, event ) {
|
||||||
|
var range = self.getSelection(),
|
||||||
|
node, parent;
|
||||||
|
// If no selection and in an empty block
|
||||||
|
if ( range.collapsed &&
|
||||||
|
rangeDoesStartAtBlockBoundary( range ) &&
|
||||||
|
rangeDoesEndAtBlockBoundary( range ) ) {
|
||||||
|
node = getStartBlockOfRange( range );
|
||||||
|
// Iterate through the block's parents
|
||||||
|
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' ) {
|
||||||
|
// AND the LI is not the first in the list
|
||||||
|
if ( node.previousSibling ) {
|
||||||
|
// Then increase the list level
|
||||||
|
event.preventDefault();
|
||||||
|
self.modifyBlocks( increaseListLevel, range );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
space: function ( self ) {
|
space: function ( self ) {
|
||||||
var range = self.getSelection();
|
var range = self.getSelection();
|
||||||
self._recordUndoState( range );
|
self._recordUndoState( range );
|
||||||
|
@ -2264,4 +2338,7 @@ proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel );
|
||||||
|
|
||||||
proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
|
proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList );
|
||||||
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
|
proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList );
|
||||||
proto.removeList = command( 'modifyBlocks', decreaseListLevel );
|
proto.removeList = command( 'modifyBlocks', removeList );
|
||||||
|
|
||||||
|
proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel );
|
||||||
|
proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel );
|
||||||
|
|
|
@ -364,26 +364,40 @@ function mergeWithBlock ( block, next, range ) {
|
||||||
|
|
||||||
function mergeContainers ( node ) {
|
function mergeContainers ( node ) {
|
||||||
var prev = node.previousSibling,
|
var prev = node.previousSibling,
|
||||||
first = node.firstChild;
|
first = node.firstChild,
|
||||||
|
isListItem = ( node.nodeName === 'LI' );
|
||||||
|
|
||||||
|
// Do not merge LIs, unless it only contains a UL
|
||||||
|
if ( isListItem && ( !first || !/^[OU]L$/.test( first.nodeName ) ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( prev && areAlike( prev, node ) && isContainer( prev ) ) {
|
if ( prev && areAlike( prev, node ) && isContainer( prev ) ) {
|
||||||
detach( node );
|
detach( node );
|
||||||
prev.appendChild( empty( node ) );
|
prev.appendChild( empty( node ) );
|
||||||
if ( first ) {
|
if ( first ) {
|
||||||
mergeContainers( first );
|
mergeContainers( first );
|
||||||
}
|
}
|
||||||
|
} else if ( isListItem ) {
|
||||||
|
prev = node.ownerDocument.createElement( 'div' );
|
||||||
|
node.insertBefore( prev, first );
|
||||||
|
fixCursor( prev );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElement ( doc, tag, props, children ) {
|
function createElement ( doc, tag, props, children ) {
|
||||||
var el = doc.createElement( tag ),
|
var el = doc.createElement( tag ),
|
||||||
attr, i, l;
|
attr, value, i, l;
|
||||||
if ( props instanceof Array ) {
|
if ( props instanceof Array ) {
|
||||||
children = props;
|
children = props;
|
||||||
props = null;
|
props = null;
|
||||||
}
|
}
|
||||||
if ( props ) {
|
if ( props ) {
|
||||||
for ( attr in props ) {
|
for ( attr in props ) {
|
||||||
el.setAttribute( attr, props[ attr ] );
|
value = props[ attr ];
|
||||||
|
if ( value !== undefined ) {
|
||||||
|
el.setAttribute( attr, props[ attr ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( children ) {
|
if ( children ) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Copyright © 2011-2013 by Neil Jenkins. MIT Licensed. */
|
/* Copyright © 2011-2013 by Neil Jenkins. MIT Licensed. */
|
||||||
|
|
||||||
( function ( doc ) {
|
( function ( doc, undefined ) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
Loading…
Reference in a new issue