mediawiki-extensions-Visual.../modules/ve/ui/ve.ui.Toolbar.js
Ed Sanders 8b09dd7650 The resurrection
By removing the transaction listeners from surface fragments we
no longer have to make sure they are always manually destroyed.

In order to retain the functionality of having fragments update
with transactions elsewhere we keep a pointer to a place in the
new complete history stack in the surface. The complete history
stack records all transactions, even undone ones.

Whenever getRange is called we replay all transactions in the
complete history (in the correct order) since the fragment was
last updated.

Also in this commit:
* Updated Format/IndentationAction to test undo(). This increases
  coverage of surface fragment behaviour.
* .range is always accessed by .getRange now, although as an
  optimisation we can use the noCopy mode when we a sure the
  returned range will not be modified.
* Added undo test to .update (previously .onTransact)

Bug: 47343
Change-Id: I9e9818da1baa8319a3002f6d74fd1aad6732a8f5
2013-04-22 12:50:23 +01:00

123 lines
3 KiB
JavaScript

/*!
* VisualEditor UserInterface Toolbar class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* UserInterface toolbar.
*
* @class
* @extends ve.EventEmitter
* @constructor
* @param {jQuery} $container
* @param {ve.Surface} surface
* @param {Array} config
*/
ve.ui.Toolbar = function VeUiToolbar( $container, surface, config ) {
// Parent constructor
ve.EventEmitter.call( this );
// Properties
this.surface = surface;
this.$ = $container;
this.$groups = $( '<div>' );
this.config = config || {};
this.onContextChangeHandler = ve.bind( this.onContextChange, this );
// Events
this.surface.getModel().on( 'contextChange', this.onContextChangeHandler );
// Initialization
this.$groups.addClass( 've-ui-toolbarGroups' );
this.$.prepend( this.$groups );
this.setup();
};
/* Inheritance */
ve.inheritClass( ve.ui.Toolbar, ve.EventEmitter );
/* Events */
/**
* @event updateState
* @see ve.dm.SurfaceFragment#getAnnotations
* @param {ve.dm.Node[]} nodes List of nodes covered by the current selection
* @param {ve.dm.AnnotationSet} full Annotations that cover all of the current selection
* @param {ve.dm.AnnotationSet} partial Annotations that cover some or all of the current selection
*/
/* Methods */
/**
* Gets the surface the toolbar controls.
*
* @method
* @returns {ve.Surface} Surface being controlled
*/
ve.ui.Toolbar.prototype.getSurface = function () {
return this.surface;
};
/**
* Handle context changes on the surface.
*
* @method
* @emits updateState
*/
ve.ui.Toolbar.prototype.onContextChange = function () {
var i, len, leafNodes,
fragment = this.surface.getModel().getFragment( null, false ),
nodes = [];
leafNodes = fragment.getLeafNodes();
for ( i = 0, len = leafNodes.length; i < len; i++ ) {
if ( len === 1 || !leafNodes[i].range || leafNodes[i].range.getLength() ) {
nodes.push( leafNodes[i].node );
}
}
this.emit( 'updateState', nodes, fragment.getAnnotations(), fragment.getAnnotations( true ) );
};
/**
* Initialize all tools and groups.
*
* @method
*/
ve.ui.Toolbar.prototype.setup = function () {
var i, j, group, $group, tool;
for ( i = 0; i < this.config.length; i++ ) {
group = this.config[i];
// Create group
$group = $( '<div class="ve-ui-toolbarGroup"></div>' )
.addClass( 've-ui-toolbarGroup-' + group.name );
if ( group.label ) {
$group.append( $( '<div class="ve-ui-toolbarLabel"></div>' ).html( group.label ) );
}
// Add tools
for ( j = 0; j < group.items.length; j++ ) {
tool = ve.ui.toolFactory.create( group.items[j], this );
if ( !tool ) {
throw new Error( 'Unknown tool: ' + group.items[j] );
}
$group.append( tool.$ );
}
// Append group
this.$groups.append( $group );
}
};
/**
* Destroys toolbar, removing event handlers and DOM elements.
*
* Call this whenever you are done using a toolbar.
*
* @method
*/
ve.ui.Toolbar.prototype.destroy = function () {
this.surface.getModel().removeListener( 'contextChange', this.onContextChangeHandler );
this.$.remove();
};