mediawiki-extensions-Visual.../modules/ve/ui/ve.ui.Toolbar.js
Timo Tijhof f06952f2f3 Refactor ve.js utilities and improve documentation
Refactor:
* ve.indexOf
  Renamed from ve.inArray.
  This was named after the jQuery method which in turn has a longer
  story about why it is so unfortunately named. It doesn't return
  a boolean, but an index. Hence the native method being called
  indexOf as well.

* ve.bind
  Renamed from ve.proxy.
  I considered making it use Function.prototype.bind if available.
  As it performs better than $.proxy (which doesn't use to the native
  bind if available). However since bind needs to be bound itself in
  order to use it detached, it turns out with the "call()" and
  "bind()"  it is slower than the $.proxy shim:
  http://jsperf.com/function-bind-shim-perf
  It would've been like this:
  ve.bind = Function.prototype.bind ?
      Function.prototype.call.bind( Function.prototype.bind ) :
      $.proxy;
  But instead sticking to ve.bind = $.proxy;

* ve.extendObject
  Documented the parts of jQuery.extend that we use. This makes it
  easier to replace in the future.

Documentation:
* Added function documentation blocks.
* Added annotations to  functions that we will be able to remove
  in the future in favour of the native methods.
  With "@until + when/how".
  In this case "ES5". Meaning, whenever we drop support for browsers
  that don't support ES5. Although in the developer community ES5 is
  still fairly fresh, browsers have been aware for it long enough
  that thee moment we're able to drop it may be sooner than we think.
  The only blocker so far is IE8. The rest of the browsers have had
  it long enough that the traffic we need to support of non-IE
  supports it.

Misc.:
* Removed 'node: true' from .jshintrc since Parsoid is no longer in
  this repo and thus no more nodejs files.
 - This unraveled two lint errors: Usage of 'module' and 'console'.
   (both were considered 'safe globals' due to nodejs, but not in
   browser code).

* Replaced usage (before renaming):
 - $.inArray -> ve.inArray
 - Function.prototype.bind -> ve.proxy
 - Array.isArray -> ve.isArray
 - [].indexOf -> ve.inArray
 - $.fn.bind/live/delegate/unbind/die/delegate -> $.fn.on/off

Change-Id: Idcf1fa6a685b6ed3d7c99ffe17bd57a7bc586a2c
2012-08-12 20:32:45 +02:00

139 lines
3.4 KiB
JavaScript

/**
* VisualEditor user interface Toolbar class.
*
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Editing toolbar.
*
* @class
* @constructor
*/
ve.ui.Toolbar = function ( $container, surfaceView, config ) {
// Inheritance TODO: Do we still need it?
ve.EventEmitter.call( this );
if ( !surfaceView ) {
return;
}
// Properties
this.surfaceView = surfaceView;
this.$ = $container;
this.$groups = $( '<div class="es-toolbarGroups"></div>' ).prependTo( this.$ );
this.tools = [];
// Update tools on selection and all transactions.
this.surfaceView.model.on( 'change', ve.bind( this.updateTools, this ) );
this.config = config || [
{ 'name': 'history', 'items' : ['undo', 'redo'] },
{ 'name': 'textStyle', 'items' : ['format'] },
{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
];
this.setup();
};
/* Methods */
/**
* Triggers update events on all tools.
*
* @method
*/
ve.ui.Toolbar.prototype.updateTools = function () {
var model = this.surfaceView.getModel(),
doc = model.getDocument(),
annotations,
nodes = [],
range = model.getSelection(),
startNode,
endNode,
tool = this,
i;
if ( range !== null ) {
if ( range.from === range.to ) {
nodes.push( doc.getNodeFromOffset( range.from ) );
} else {
startNode = doc.getNodeFromOffset( range.from );
endNode = doc.getNodeFromOffset ( range.end );
if(startNode.type === 'document' || endNode.type === 'document') {
// Clear state
for ( i = 0; i < this.tools.length; i++ ) {
this.tools[i].clearState();
}
return;
}
// These should be different, alas just in case.
if ( startNode === endNode ) {
nodes.push( startNode );
} else {
model.getDocument().getDocumentNode().traverseLeafNodes( function ( node ) {
nodes.push( node );
if( node === endNode ) {
return false;
}
}, startNode );
}
}
if ( range.getLength() > 0 ) {
annotations = doc.getAnnotationsFromRange( range );
} else {
// Clear context
tool.surfaceView.contextView.clear();
annotations = doc.getAnnotationsFromOffset(
doc.getNearestContentOffset( range.start - 1 )
);
}
// Update state
for ( i = 0; i < this.tools.length; i++ ) {
this.tools[i].updateState( annotations, nodes );
}
} else {
// Clear state
for ( i = 0; i < this.tools.length; i++ ) {
this.tools[i].clearState();
}
}
};
ve.ui.Toolbar.prototype.getSurfaceView = function () {
return this.surfaceView;
};
ve.ui.Toolbar.prototype.setup = function () {
var i, j, $group, tool, toolDefintion;
for ( i = 0; i < this.config.length; i++ ) {
$group = $( '<div>' )
.addClass( 'es-toolbarGroup' )
.addClass( 'es-toolbarGroup-' + this.config[i].name );
if ( this.config[i].label ) {
$group.append(
$( '<div>' ).addClass( 'es-toolbarLabel' ).html( this.config[i].label )
);
}
for ( j = 0; j < this.config[i].items.length; j++ ) {
toolDefintion = ve.ui.Tool.tools[ this.config[i].items[j] ];
if ( toolDefintion ) {
tool = new toolDefintion.constructor(
this, toolDefintion.name, toolDefintion.title, toolDefintion.data
);
this.tools.push( tool );
$group.append( tool.$ );
}
}
this.$groups.append( $group );
}
};
ve.extendClass( ve.ui.Toolbar, ve.EventEmitter );