mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-27 04:06:51 +00:00
Merge "First stab at list indent/outdent"
This commit is contained in:
commit
5d9ca9a4b8
|
@ -46,45 +46,195 @@ ve.ui.IndentationButtonTool.prototype.onClick = function() {
|
|||
};
|
||||
|
||||
ve.ui.IndentationButtonTool.prototype.indent = function( listItems ) {
|
||||
// FIXME old code, doesn't work
|
||||
var surface = this.toolbar.surfaceView,
|
||||
styles,
|
||||
i,
|
||||
tx;
|
||||
var surfaceModel = this.toolbar.getSurfaceView().getModel(),
|
||||
documentModel = surfaceModel.getDocument(),
|
||||
selection = surfaceModel.getSelection(),
|
||||
groups = documentModel.getCoveredSiblingGroups( selection );
|
||||
|
||||
for ( i = 0; i < listItems.length; i++ ) {
|
||||
styles = listItems[i].getElementAttribute( 'styles' );
|
||||
if ( styles.length < 6 ) {
|
||||
tx = surface.model.getDocument().prepareElementAttributeChange(
|
||||
surface.documentView.model.getOffsetFromNode( listItems[i], false ),
|
||||
'styles',
|
||||
styles.concat( styles[styles.length - 1] )
|
||||
);
|
||||
surface.model.change( tx );
|
||||
function indentListItem( listItem ) {
|
||||
/*
|
||||
* Indenting a list item is done as follows:
|
||||
* 1. Wrap the listItem in a list and a listItem (<li> --> <li><ul><li>)
|
||||
* 2. Merge this wrapped listItem into the previous listItem if present
|
||||
* (<li>Previous</li><li><ul><li>This --> <li>Previous<ul><li>This)
|
||||
* 3. If this results in the wrapped list being preceded by another list,
|
||||
* merge those lists.
|
||||
*/
|
||||
var tx,
|
||||
listType = listItem.getParent().getAttribute( 'style' ),
|
||||
listItemRange = listItem.getOuterRange(),
|
||||
innerListItemRange,
|
||||
outerListItemRange,
|
||||
mergeStart,
|
||||
mergeEnd;
|
||||
|
||||
// CAREFUL: after initializing the variables above, we cannot use the model tree!
|
||||
// The first transaction will cause rebuilds so the nodes we have references to now
|
||||
// will be detached and useless after the first transaction. Instead, inspect
|
||||
// documentModel.data to find out things about the current structure.
|
||||
|
||||
// (1) Wrap the listItem in a list and a listItem
|
||||
tx = ve.dm.Transaction.newFromWrap( documentModel,
|
||||
listItemRange,
|
||||
[],
|
||||
[ { 'type': 'listItem' }, { 'type': 'list', 'attributes': { 'style': listType } } ],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
// tx.translateRange( innerListItemRange ) doesn't do what we want
|
||||
innerListItemRange = ve.Range.newFromTranslatedRange( listItemRange, 2 );
|
||||
outerListItemRange = new ve.Range( listItemRange.start, listItemRange.end + 2 );
|
||||
|
||||
// (2) Merge the listItem into the previous listItem (if there is one)
|
||||
if (
|
||||
documentModel.data[listItemRange.start].type === 'listItem' &&
|
||||
documentModel.data[listItemRange.start - 1].type === '/listItem'
|
||||
) {
|
||||
mergeStart = listItemRange.start - 1;
|
||||
mergeEnd = listItemRange.start + 1;
|
||||
// (3) If this results in adjacent lists, merge those too
|
||||
if (
|
||||
documentModel.data[mergeEnd].type === 'list' &&
|
||||
documentModel.data[mergeStart - 1].type === '/list'
|
||||
) {
|
||||
mergeStart--;
|
||||
mergeEnd++;
|
||||
}
|
||||
tx = ve.dm.Transaction.newFromRemoval( documentModel, new ve.Range( mergeStart, mergeEnd ) );
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
innerListItemRange = tx.translateRange( innerListItemRange );
|
||||
outerListItemRange = tx.translateRange( outerListItemRange );
|
||||
}
|
||||
|
||||
// TODO If this listItem has a child list, split&unwrap it
|
||||
|
||||
surfaceModel.change( null, selection );
|
||||
}
|
||||
|
||||
for ( var i = 0; i < groups.length; i++ ) {
|
||||
group = groups[i];
|
||||
if ( group.grandparent && group.grandparent.getType() === 'list' ) {
|
||||
// FIXME this doesn't work when trying to work with multiple list items
|
||||
indentListItem( group.parent );
|
||||
}
|
||||
}
|
||||
surface.emitCursor();
|
||||
};
|
||||
|
||||
ve.ui.IndentationButtonTool.prototype.outdent = function( listItems ) {
|
||||
// FIXME old code, doesn't work
|
||||
var surface = this.toolbar.surfaceView,
|
||||
styles,
|
||||
i,
|
||||
tx;
|
||||
var surfaceModel = this.toolbar.getSurfaceView().getModel(),
|
||||
documentModel = surfaceModel.getDocument(),
|
||||
selection = surfaceModel.getSelection(),
|
||||
groups = documentModel.getCoveredSiblingGroups( selection );
|
||||
|
||||
for ( i = 0; i < listItems.length; i++ ) {
|
||||
styles = listItems[i].getElementAttribute( 'styles' );
|
||||
if ( styles.length > 1 ) {
|
||||
tx = surface.model.getDocument().prepareElementAttributeChange(
|
||||
surface.documentView.model.getOffsetFromNode( listItems[i], false ),
|
||||
'styles',
|
||||
styles.slice( 0, styles.length - 1 )
|
||||
function outdentListItem( listItem ) {
|
||||
/*
|
||||
* Outdenting a list item is done as follows:
|
||||
* 1. Split the parent list to isolate the listItem in its own list
|
||||
* 1a. Split the list before the listItem if it's not the first child
|
||||
* 1b. Split the list after the listItem if it's not the last child
|
||||
* 2. If this isolated list's parent is not a listItem, unwrap the listItem and the isolated list, and stop.
|
||||
* 3. Split the parent listItem to isolate the list in its own listItem
|
||||
* 3a. Split the listItem before the list if it's not the first child
|
||||
* 3b. Split the listItem after the list if it's not the last child
|
||||
* 4. Unwrap the now-isolated listItem and the isolated list
|
||||
*/
|
||||
// TODO child list handling, gotta figure that out
|
||||
var tx,
|
||||
list = listItem.getParent(),
|
||||
listType = list.getAttribute( 'style' ),
|
||||
listElement = list.getClonedElement(),
|
||||
grandParentType = list.getParent().getType(),
|
||||
listItemRange = listItem.getOuterRange(),
|
||||
splitListRange;
|
||||
|
||||
// CAREFUL: after initializing the variables above, we cannot use the model tree!
|
||||
// The first transaction will cause rebuilds so the nodes we have references to now
|
||||
// will be detached and useless after the first transaction. Instead, inspect
|
||||
// documentModel.data to find out things about the current structure.
|
||||
|
||||
// (1) Split the listItem into a separate list
|
||||
if ( documentModel.data[listItemRange.start - 1].type !== 'list' ) {
|
||||
// (1a) listItem is not the first child, split the list before listItem
|
||||
tx = ve.dm.Transaction.newFromInsertion( documentModel, listItemRange.start,
|
||||
[ { 'type': '/list' }, listElement ]
|
||||
);
|
||||
surface.model.change( tx );
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
// tx.translateRange( listItemRange ) doesn't do what we want
|
||||
listItemRange = ve.Range.newFromTranslatedRange( listItemRange, 2 );
|
||||
}
|
||||
if ( documentModel.data[listItemRange.end].type !== '/list' ) {
|
||||
// (1b) listItem is not the last child, split the list after listItem
|
||||
tx = ve.dm.Transaction.newFromInsertion( documentModel, listItemRange.end,
|
||||
[ { 'type': '/list' }, listElement ]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
// listItemRange is not affected by this transaction
|
||||
}
|
||||
splitListRange = new ve.Range( listItemRange.start - 1, listItemRange.end + 1 );
|
||||
|
||||
if ( grandParentType !== 'listItem' ) {
|
||||
// The user is trying to unindent a list item that's not nested
|
||||
// (2) Unwrap both the list and the listItem, dumping the listItem's contents
|
||||
// into the list's parent
|
||||
tx = ve.dm.Transaction.newFromWrap( documentModel,
|
||||
new ve.Range( listItemRange.start + 1, listItemRange.end - 1 ),
|
||||
[ { 'type': 'list' }, { 'type': 'listItem' } ],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
} else {
|
||||
// (3) Split the list away from parentListItem into its own listItem
|
||||
// TODO factor common split logic somehow?
|
||||
if ( documentModel.data[splitListRange.start - 1].type !== 'listItem' ) {
|
||||
// (3a) Split parentListItem before list
|
||||
tx = ve.dm.Transaction.newFromInsertion( documentModel, splitListRange.start,
|
||||
[ { 'type': '/listItem' }, { 'type': 'listItem' } ]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
// tx.translateRange( splitListRange ) doesn't do what we want
|
||||
splitListRange = ve.Range.newFromTranslatedRange( splitListRange, 2 );
|
||||
}
|
||||
if ( documentModel.data[splitListRange.end].type !== '/listItem' ) {
|
||||
// (3b) Split parentListItem after list
|
||||
tx = ve.dm.Transaction.newFromInsertion( documentModel, splitListRange.end,
|
||||
[ { 'type': '/listItem' }, { 'type': 'listItem' } ]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
// splitListRange is not affected by this transaction
|
||||
}
|
||||
|
||||
// (4) Unwrap the list and its containing listItem
|
||||
tx = ve.dm.Transaction.newFromWrap( documentModel,
|
||||
new ve.Range( splitListRange.start + 1, splitListRange.end - 1 ),
|
||||
[ { 'type': 'listItem' }, { 'type': 'list' } ],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
surfaceModel.change( tx );
|
||||
selection = tx.translateRange( selection );
|
||||
}
|
||||
|
||||
surfaceModel.change( null, selection );
|
||||
}
|
||||
|
||||
for ( var i = 0; i < groups.length; i++ ) {
|
||||
group = groups[i];
|
||||
if ( group.grandparent && group.grandparent.getType() === 'list' ) {
|
||||
// FIXME this doesn't work when trying to work with multiple list items
|
||||
outdentListItem( group.parent );
|
||||
}
|
||||
}
|
||||
surface.emitCursor();
|
||||
};
|
||||
|
||||
ve.ui.IndentationButtonTool.prototype.updateState = function( annotations, nodes ) {
|
||||
|
@ -111,8 +261,6 @@ ve.ui.IndentationButtonTool.prototype.updateState = function( annotations, nodes
|
|||
|
||||
/* Registration */
|
||||
|
||||
// Commented out because these don't work yet
|
||||
/*
|
||||
ve.ui.Tool.tools.indent = {
|
||||
'constructor': ve.ui.IndentationButtonTool,
|
||||
'name': 'indent',
|
||||
|
@ -124,7 +272,7 @@ ve.ui.Tool.tools.outdent = {
|
|||
'name': 'outdent',
|
||||
'title': ve.msg( 'visualeditor-indentationbutton-outdent-tooltip' ),
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
|
|
Loading…
Reference in a new issue