mediawiki-extensions-Visual.../modules/ve/ui/dialogs/ve.ui.MWMetaDialog.js
Trevor Parscal 7233ea8f1b EventEmitter API cleanup
The EventEmitter API we inherited from Node.js and then bastardized was
getting awkward and cumbersome. The number of uses of ve.bind was getting
out of control, and removing events meant caching the bound method in a
property. Many of the "features" of EventEmitter wasn't even being used,
some causing overhead, others just causing bloat. This change cleans up
how EventEmitter is used throughout the codebase.

The new event emitter API includes:
* emit - identical to the previous API, no longer throws an error if you
  emit error without a handler
* once - identical to the previous API, still introduces a wrapper* on -
  compatible with the previous API but has some new features
* off - identical to removeListener in the previous API
* connect - very similar to addListenerMethods but doesn't wrap callbacks
  in closures anymore
* disconnect - new, basically the opposite of addListenerMethods

Another change that is made in this commit is mixing in rather than
inheriting from EventEmitter.

Finally, there are changes throughout the codebase anywhere
connect/disconnect could be used.

Change-Id: Ic3085d39172a8a719ce7f036690f673e59848d3a
2013-05-02 15:05:59 -07:00

231 lines
5.7 KiB
JavaScript

/*!
* VisualEditor user interface MWMetaDialog class.
*
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Document dialog.
*
* @class
* @extends ve.ui.PagedDialog
*
* @constructor
* @param {ve.Surface} surface
*/
ve.ui.MWMetaDialog = function VeUiMWMetaDialog( surface ) {
// Parent constructor
ve.ui.PagedDialog.call( this, surface );
// Properties
this.metaList = surface.getModel().metaList;
// Events
this.metaList.connect( this, {
'insert': 'onMetaListInsert',
'remove': 'onMetaListRemove'
} );
};
/* Inheritance */
ve.inheritClass( ve.ui.MWMetaDialog, ve.ui.PagedDialog );
/* Static Properties */
ve.ui.MWMetaDialog.static.titleMessage = 'visualeditor-dialog-meta-title';
ve.ui.MWMetaDialog.static.icon = 'settings';
/* Methods */
/**
* Handle frame ready events.
*
* @method
*/
ve.ui.MWMetaDialog.prototype.onOpen = function () {
var surfaceModel = this.surface.getModel();
// Force all previous transactions to be separate from this history state
surfaceModel.breakpoint();
surfaceModel.stopHistoryTracking();
// Parent method
ve.ui.PagedDialog.prototype.onOpen.call( this );
};
/**
* Handle frame ready events.
*
* @method
* @param {string} action Action that caused the window to be closed
*/
ve.ui.MWMetaDialog.prototype.onClose = function ( action ) {
var surfaceModel = this.surface.getModel();
// Parent method
ve.ui.PagedDialog.prototype.onOpen.call( this );
// Place transactions made while dialog was open in a common history state
surfaceModel.breakpoint();
// Undo everything done in the dialog and prevent redoing those changes
if ( action === 'cancel' ) {
surfaceModel.undo();
surfaceModel.truncateUndoStack();
}
// Return to normal tracking behavior
surfaceModel.startHistoryTracking();
};
/**
* Handle frame ready events.
*
* @method
*/
ve.ui.MWMetaDialog.prototype.initialize = function () {
// Call parent method
ve.ui.PagedDialog.prototype.initialize.call( this );
// Properties
this.categoryWidget = new ve.ui.MWCategoryWidget( {
'$$': this.$$, '$overlay': this.$overlay
} );
// Events
this.categoryWidget.connect( this, {
'newCategory': 'onNewCategory',
'updateSortkey': 'onUpdateSortKey'
} );
// Initialization
this.categoryWidget.addItems( this.getCategoryItems() );
this.addPage( 'categories', 'Categories', 'tag' )
.addPage( 'languages', 'Languages', 'language' );
this.pages.categories.$.append( this.categoryWidget.$ );
};
/**
* Gets array of category items from meta list
*
* @method
* @returns {Object[]} items
*/
ve.ui.MWMetaDialog.prototype.getCategoryItems = function () {
var i,
items = [],
categories = this.metaList.getItemsInGroup( 'MWcategory' );
// Loop through MwCategories and build out items
for ( i = 0; i < categories.length; i++ ) {
items.push( this.getCategoryItemFromMetaListItem( categories[i] ) );
}
return items;
};
/**
* Gets category item from meta list item
*
* @method
* @param {Object} ve.dm.MWCategoryMetaItem
* @returns {Object} item
*/
ve.ui.MWMetaDialog.prototype.getCategoryItemFromMetaListItem = function ( metaItem ) {
return {
'name': metaItem.element.attributes.category,
'value': metaItem.element.attributes.category.split( ':' )[1],
// TODO: sortkey is lcase, make consistent throughout CategoryWidget
'sortKey': metaItem.element.attributes.sortkey,
'metaItem': metaItem
};
};
/**
* Get metaList like object to insert from item
*
* @method
* @param {Object} item category widget item
* @returns {Object} metaBase
*/
ve.ui.MWMetaDialog.prototype.getCategoryItemForInsertion = function ( item ) {
return {
'attributes': { 'category': item.name, 'sortkey': item.sortKey || '' },
'type': 'MWcategory'
};
};
/**
* Inserts new category into meta list
*
* @method
* @param {Object} item
*/
ve.ui.MWMetaDialog.prototype.onNewCategory = function ( item ) {
// Insert new metaList item
this.insertMetaListItem( this.getCategoryItemForInsertion( item ) );
};
/**
* Removes and re-inserts updated category widget item
*
* @method
* @param {Object} item
*/
ve.ui.MWMetaDialog.prototype.onUpdateSortKey = function ( item ) {
// Store the offset and index before removing
var offset = item.metaItem.offset,
index = item.metaItem.index;
item.metaItem.remove();
// It would seem as if insertItem happens before the onRemove event is sent to CategoryWidget,
// Remove the reference there so it doesn't try to get removed again onMetaListInsert
delete this.categoryWidget.categories[item.name];
// Insert updated meta item at same offset and index
this.metaList.insertMeta( this.getCategoryItemForInsertion( item ), offset, index );
};
/**
* Bound to MetaList insert event for adding meta dialog components.
*
* @method
* @param {Object} ve.dm.MetaItem
*/
ve.ui.MWMetaDialog.prototype.onMetaListInsert = function ( metaItem ) {
// Responsible for adding UI components.
if ( metaItem.element.type === 'MWcategory' ) {
this.categoryWidget.addItems( [ this.getCategoryItemFromMetaListItem( metaItem ) ] );
}
};
/**
* Bound to MetaList insert event for removing meta dialog components.
*
* @method
* @param {Object} ve.dm.MetaItem
*/
ve.ui.MWMetaDialog.prototype.onMetaListRemove = function ( metaItem ) {
var item;
if ( metaItem.element.type === 'MWcategory' ) {
item = this.getCategoryItemFromMetaListItem( metaItem );
this.categoryWidget.removeItems( [item.value] );
}
};
/**
* Inserts a meta list item
*
* @param {Object} metaBase meta list insert object
*/
ve.ui.MWMetaDialog.prototype.insertMetaListItem = function ( metaBase ) {
var offset = this.surface.getModel().getDocument().getData().length;
this.metaList.insertMeta( metaBase, offset );
};
/* Registration */
ve.ui.dialogFactory.register( 'mwMeta', ve.ui.MWMetaDialog );