mediawiki-extensions-Visual.../modules/ve/ui/tools/ve.ui.ListButtonTool.js
Trevor Parscal 85b807ed5d Changed to using structured lists
This makes it possible to get identical rendering in the editor, but may make other things more complex. The Wikitext serializer is no longer compatible for rendering lists so it's been stubbed out. Also the way the toolbar works with lists is broken, so that's been disabled. The HTML serializer has been fixed to work correctly and no-longer-used styles have been removed.

Change-Id: If156f55068b1f6d229b3fa789164f28b2e3dfc76
2012-04-05 14:32:08 -07:00

325 lines
8.4 KiB
JavaScript

/**
* Creates an ve.ui.ListButtonTool object.
*
* @class
* @constructor
* @extends {ve.ui.ButtonTool}
* @param {ve.ui.Toolbar} toolbar
* @param {String} name
*/
ve.ui.ListButtonTool = function( toolbar, name, title, data ) {
// Inheritance
ve.ui.ButtonTool.call( this, toolbar, name, title );
// Properties
this.data = data;
this.nodes = [];
};
/* Methods */
ve.ui.ListButtonTool.prototype.list = function( nodes, style ) {
var surface = this.toolbar.surfaceView,
selection = surface.currentSelection.clone(),
stack = [],
stacks = [],
listItems = [],
parent,
styles,
insertAt,
removeLength,
data,
tx,
i,
j;
for( i = 0; i < nodes.length; i++ ) {
parent = nodes[i].getParent();
if ( parent.getElementType() === 'listItem' ) {
if ( stack.length > 0 ) {
stacks.push( stack );
stack = [];
}
listItems.push( parent );
} else {
if( stack.length > 0 ) {
if ( parent === stack[stack.length - 1].getParent() ) {
stack.push( nodes[i] );
} else {
stacks.push( stack );
stack = [ nodes[i] ];
}
} else {
stack.push( nodes[i] );
}
}
}
if( stack.length > 0 ) {
stacks.push( stack );
}
if ( stacks.length > 0 ) {
if ( selection.from === selection.to ) {
selection.from += 2;
selection.to += 2;
} else {
if ( nodes[0].getParent().getElementType() != 'listItem' ) {
if ( selection.from < selection.to ) {
selection.from += 2;
} else {
selection.to += 2;
}
}
if ( selection.from < selection.to ) {
selection.to += (stacks.length * 2) + (nodes.length - listItems.length - 1) * 2;
} else {
selection.from += (stacks.length * 2) + (nodes.length - listItems.length - 1) * 2;
}
}
}
for ( i = 0; i < listItems.length; i++ ) {
styles = listItems[i].getElementAttribute( 'styles' );
if ( styles[styles.length - 1] !== style ) {
tx = surface.model.getDocument().prepareElementAttributeChange(
surface.documentView.model.getOffsetFromNode( listItems[i], false ),
'styles',
styles.slice( 0, styles.length - 1 ).concat( style )
);
surface.model.transact( tx );
}
}
for ( i = 0; i < stacks.length; i++ ) {
removeLength = 0;
insertAt = surface.documentView.model.getOffsetFromNode( stacks[i][0], false );
data = [ { 'type': 'list' } ];
for( j = 0; j < stacks[i].length; j++ ) {
removeLength += stacks[i][j].getElementLength();
data = data
.concat( [ {
'type': 'listItem',
'attributes' : { 'styles': [ this.name ] }
} ] )
//.concat( stacks[i][j].getElementData() )
.concat( [ { 'type': 'paragraph' } ] )
.concat( stacks[i][j].getContentData() )
.concat( [ { 'type': '/paragraph' } ] )
.concat( [ { 'type': '/listItem' } ] );
}
data = data.concat( [ { 'type': '/list' } ] );
tx = surface.model.getDocument().prepareInsertion( insertAt, data );
surface.model.transact( tx );
tx = surface.model.getDocument().prepareRemoval(
new ve.Range( insertAt + data.length, insertAt + removeLength + data.length )
);
surface.model.transact( tx );
}
surface.model.select( selection, true );
surface.emitCursor();
};
ve.ui.ListButtonTool.prototype.unlist = function( nodes ) {
var listItems = [],
listItem,
i;
for( i = 0; i < nodes.length; i++ ) {
listItem = nodes[i].getParent();
if ( listItems.length > 0 ) {
if (listItem != listItems[listItems.length - 1]) {
listItems.push( listItem );
}
} else {
listItems.push( listItem );
}
}
var stacks = [],
stack = {
first: false,
last: false,
nodes: [],
offset: 0,
length: 0
},
surface = this.toolbar.surfaceView,
selection = surface.currentSelection.clone(),
from = 0,
to = 0;
for( i = 0; i < listItems.length; i++ ) {
if( stack.nodes.length > 0 ) {
if ( stack.nodes[stack.nodes.length - 1].getParent() != listItems[i].getParent() ) {
stacks.push( stack );
stack = {
first: false,
last: false,
nodes: [],
offset: 0,
length: 0
};
}
}
if ( listItems[i].getParent().indexOf( listItems[i] ) === 0 ) {
stack.first = true;
}
if ( listItems[i].getParent().indexOf( listItems[i] ) === listItems[i].getParent().children.length - 1 ) {
stack.last = true;
}
if( stack.nodes.length === 0 ){
stack.offset = surface.documentView.model.getOffsetFromNode(listItems[i], false);
}
stack.length += listItems[i].getElementLength();
stack.nodes.push( listItems[i] );
}
if( stack.nodes.length > 0 ) {
stacks.push(stack);
}
var tx,
j,
extra,
data;
for( i = stacks.length - 1; i >= 0; i-- ) {
stack = stacks[i];
data = [];
for( j = 0; j < stack.nodes.length; j++ ) {
data = data.concat( stack.nodes[j].getContentData() );
}
if ( stack.first === true && stack.last === true ) {
tx = surface.model.getDocument().prepareRemoval(
new ve.Range( stack.offset - 1 /* list */, stack.offset + stack.length + 1 /* /list */ )
);
surface.model.transact( tx );
tx = surface.model.getDocument().prepareInsertion( stack.offset - 1, data );
surface.model.transact( tx );
from = -2;
to += -(stack.nodes.length * 2);
} else if ( stack.first === true && stack.last === false ) {
tx = surface.model.getDocument().prepareRemoval(
new ve.Range( stack.offset, stack.offset + stack.length )
);
surface.model.transact( tx );
tx = surface.model.getDocument().prepareInsertion( stack.offset - 1, data );
surface.model.transact( tx );
from = -2;
to += -(stack.nodes.length * 2);
} else if ( stack.first === false && stack.last === true ) {
tx = surface.model.getDocument().prepareRemoval(
new ve.Range( stack.offset, stack.offset + stack.length )
);
surface.model.transact( tx );
tx = surface.model.getDocument().prepareInsertion( stack.offset + 1, data );
surface.model.transact( tx );
to += -(stack.nodes.length * 2);
to += 2;
} else if ( stack.first === false && stack.last === false ) {
var parent = stack.nodes[0].getParent();
var parentOffset = surface.documentView.model.getOffsetFromNode( parent, false );
var parentLength = parent.getElementLength();
tx = surface.model.getDocument().prepareRemoval(
new ve.Range( stack.offset, stack.offset + stack.length )
);
surface.model.transact( tx );
var remainingRange = new ve.Range( stack.offset, parentOffset + parentLength - stack.length - 1 );
var remainingData = surface.model.getDocument().getData( remainingRange );
tx = surface.model.getDocument().prepareRemoval( remainingRange );
surface.model.transact( tx );
tx = surface.model.getDocument().prepareInsertion(
stack.offset + 1,
[ { 'type': 'list' } ].concat( remainingData ).concat( [ { 'type': '/list' } ] )
);
surface.model.transact( tx );
tx = surface.model.getDocument().prepareInsertion( stack.offset + 1, data );
surface.model.transact( tx );
to += -(stack.nodes.length * 2);
to += 2;
}
}
if ( selection.from === selection.to ) {
selection.from += from;
selection.to += from;
} else {
if ( selection.to > selection.from ) {
selection.from += from;
selection.to += to;
} else {
selection.to += from;
selection.from += to;
}
}
surface.model.select( selection, true );
surface.emitCursor();
};
ve.ui.ListButtonTool.prototype.onClick = function() {
this.toolbar.surfaceView.model.breakpoint();
if ( !this.$.hasClass( 'es-toolbarButtonTool-down' ) ) {
this.list( this.nodes, this.name );
} else {
this.unlist( this.nodes );
}
this.toolbar.surfaceView.model.breakpoint();
};
ve.ui.ListButtonTool.prototype.updateState = function( annotations, nodes ) {
/*
* XXX: Disabled for now because lists work differently now (they are structured, not flat)
*
function areListItemsOfStyle( nodes, style ) {
var parent, styles;
for( var i = 0; i < nodes.length; i++ ) {
parent = nodes[i].getParent();
if ( parent.getElementType() !== 'listItem' ) {
return false;
}
styles = parent.getElementAttribute( 'styles' );
if ( styles[ styles.length - 1] !== style ) {
return false;
}
}
return true;
}
this.nodes = nodes;
if ( areListItemsOfStyle( this.nodes, this.name ) ) {
this.$.addClass( 'es-toolbarButtonTool-down' );
} else {
this.$.removeClass( 'es-toolbarButtonTool-down' );
}
*/
};
/* Registration */
ve.ui.Tool.tools.number = {
'constructor': ve.ui.ListButtonTool,
'name': 'number',
'title': 'Numbered list'
};
ve.ui.Tool.tools.bullet = {
'constructor': ve.ui.ListButtonTool,
'name': 'bullet',
'title': 'Bulleted list'
};
/* Inheritance */
ve.extendClass( ve.ui.ListButtonTool, ve.ui.ButtonTool );