mediawiki-extensions-Visual.../modules/ve/ui/tools/ve.ui.FormatDropdownTool.js
Trevor Parscal a52d7ff65a Finally getting rid of all the es-* stuff and other cruft
* Switched a lot of classes from es-* to ve-ui-*
* Removed all the DOM structure left over from the old sandbox demo
* Got rid of transparent backgrounds
* Added menu font-size rule to stand-alone target
* Moved some rules around that were in the wrong places
* Got rid of some unused/unneeded methods in the mw target (attach and detach surface methods)
* Added active class to context icon with shallower shadow effect so it doesn't break your spacial perception when you click on it
* Renamed the iframe and iframe wrapper elements so it's easier to see where they came from
* Removed unused CSS rules
* Fixed some uses of prop( 'class', … ) to addClass

Change-Id: I54a660ca0baf0baa4463faca7a1edcf648130b6b
2012-08-28 13:59:35 -07:00

217 lines
6 KiB
JavaScript

/**
* VisualEditor user interface FormatDropdownTool class.
*
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Creates an ve.FormatDropdownTool object.
*
* @class
* @constructor
* @extends {ve.ui.DropdownTool}
* @param {ve.ui.Toolbar} toolbar
* @param {String} name
* @param {Object[]} items
*/
ve.FormatDropdownTool = function ( toolbar, name, title ) {
// Inheritance
ve.ui.DropdownTool.call( this, toolbar, name, title, [
{
'name': 'paragraph',
'label': ve.msg( 'visualeditor-formatdropdown-format-paragraph' ),
'type' : 'paragraph'
},
{
'name': 'heading-1',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading1' ),
'type' : 'heading',
'attributes': { 'level': 1 }
},
{
'name': 'heading-2',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading2' ),
'type' : 'heading',
'attributes': { 'level': 2 }
},
{
'name': 'heading-3',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading3' ),
'type' : 'heading',
'attributes': { 'level': 3 }
},
{
'name': 'heading-4',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading4' ),
'type' : 'heading',
'attributes': { 'level': 4 }
},
{
'name': 'heading-5',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading5' ),
'type' : 'heading',
'attributes': { 'level': 5 }
},
{
'name': 'heading-6',
'label': ve.msg( 'visualeditor-formatdropdown-format-heading6' ),
'type' : 'heading',
'attributes': { 'level': 6 }
},
{
'name': 'preformatted',
'label': ve.msg( 'visualeditor-formatdropdown-format-preformatted' ),
'type' : 'preformatted'
}
] );
};
/* Methods */
ve.FormatDropdownTool.splitAndUnwrap = function ( model, list, firstItem, lastItem, selection ) {
var doc = model.getDocument(),
start = firstItem.getOuterRange().start,
end = lastItem.getOuterRange().end,
tx;
// First split the list before, if needed
if ( list.indexOf( firstItem ) > 0 ) {
tx = ve.dm.Transaction.newFromInsertion(
doc, start, [{ 'type': '/list' }, list.getClonedElement()]
);
start += 2;
end += 2;
selection = tx.translateRange( selection );
model.change( tx, selection );
}
// Split the list after, if needed
if ( list.indexOf( lastItem ) < list.getChildren().length - 1 ) {
tx = ve.dm.Transaction.newFromInsertion(
doc, end, [{ 'type': '/list' }, list.getClonedElement()]
);
selection = tx.translateRange( selection );
model.change( tx, selection );
}
// Unwrap the list
tx = ve.dm.Transaction.newFromWrap( doc, new ve.Range( start, end ),
[{ 'type': 'list' }], [], [{ 'type': 'listItem' }], []
);
selection = tx.translateRange( selection );
model.change( tx, selection );
return selection;
};
ve.FormatDropdownTool.prototype.onSelect = function ( item ) {
var selected, prevList, firstInList, lastInList, i, contentBranch, listItem, txs,
surfaceView = this.toolbar.getSurfaceView(),
model = surfaceView.getModel(),
selection = model.getSelection(),
doc = model.getDocument();
if ( item.type !== 'paragraph' ) {
// We can't have headings or pre's in a list, so if we're trying to convert
// things that are in lists to a heading or a pre, split the list
selected = doc.selectNodes( selection, 'leaves' );
for ( i = 0; i < selected.length; i++ ) {
contentBranch = selected[i].node.isContent() ?
selected[i].node.getParent() :
selected[i].node;
// Check if it's in a list
listItem = contentBranch;
while ( listItem && listItem.getType() !== 'listItem' ) {
listItem = listItem.getParent();
}
if ( !listItem || listItem.getParent() !== prevList ) {
// Not in a list or in a different list
if ( prevList ) {
// Split and unwrap prevList
selection = ve.FormatDropdownTool.splitAndUnwrap(
model, prevList, firstInList, lastInList, selection
);
}
if ( listItem ) {
prevList = listItem.getParent();
firstInList = listItem;
lastInList = firstInList;
}
} else {
// This node is in the current list
lastInList = listItem;
}
}
if ( prevList ) {
// Split and unwrap prevList
selection = ve.FormatDropdownTool.splitAndUnwrap(
model, prevList, firstInList, lastInList, selection
);
}
}
txs = ve.dm.Transaction.newFromContentBranchConversion(
doc,
selection,
item.type,
item.attributes
);
model.change( txs, selection );
surfaceView.showSelection( selection );
};
ve.FormatDropdownTool.prototype.getMatchingMenuItems = function ( nodes ) {
var i, j, nodeType, nodeAttributes, item, key,
matches = [],
items = this.menuView.getItems();
for ( i = 0; i < nodes.length; i++ ) {
nodeType = nodes[i].getType();
nodeAttributes = nodes[i].getAttributes();
// Outer loop continue point
itemLoop:
for ( j = 0; j < items.length; j++ ) {
item = items[j];
if ( item.type === nodeType ) {
if ( item.attributes && nodeAttributes ) {
// Compare attributes
for ( key in item.attributes ) {
if (
// Node must have all the required attributes
!( key in nodeAttributes ) ||
// Use weak comparison because numbers sometimes come through as strings
item.attributes[key] !== nodeAttributes[key]
) {
// Skip to the next menu item
continue itemLoop;
}
}
} else if ( item.attributes && !nodeAttributes ) {
// Node is required to have certain attributes but doesn't
// have any, so it doesn't match
continue itemLoop;
}
matches.push( item );
}
}
}
return matches;
};
ve.FormatDropdownTool.prototype.updateState = function ( annotations, nodes ) {
if ( nodes.length ) {
var items = this.getMatchingMenuItems( nodes );
if ( items.length === 1 ) {
this.$labelText.text( items[0].label );
} else {
this.$labelText.html( '&nbsp;' );
}
}
};
/* Registration */
ve.ui.Tool.tools.format = {
'constructor': ve.FormatDropdownTool,
'name': 'format',
'title': ve.msg( 'visualeditor-formatdropdown-title' )
};
/* Inheritance */
ve.extendClass( ve.FormatDropdownTool, ve.ui.DropdownTool );