OOUI

ooui is a modern UI toolkit for browsers.

It drives the dialogs, toolbars, and much of the other chrome of the VisualEditor.

Dependencies
Just add  to the dependencies of your JavaScript ResourceLoader module(s).

As of October 2013, install extension:VisualEditor to get ooui, irrespective of whether your wiki enables VE. Eventually ooui will be a stand-alone distribution that will be incorporated into MediaWiki core, as core jQuery is.

Initially ooui will be just one module, unlike the many RL modules of, say, jquery.ui

ooui has a lightweight classing model for UI elements, which it gets from  

ooui assumes core jQuery is in window scope. It doesn't assume MediaWiki modules, not even MediaWiki localization.

Browser compatibility

 * oojs
 * uses ECMAScript 5 features, so a polyfill (such as es5-shim or augment) will need to be introduced for earlier browsers that don't have these features.


 * ooui
 * browser compatibility TBD. It uses iframes for dialogs. (VisualEditor's restricted browser compatibility stems from its taxing use of the browser's   feature, not its use of ooui.).

oojs

 * See generated documentation

oojs provides
 * class inheritance
 * class mixin
 * event emitters (similar to Nodejs's emitter)
 * mix this in and you get
 * for receiving emitted events, and  to stop receiving
 * to connect multiple names of events to your on handlers, also binding  correctly. It's a nice shortcut, and there's a corresponding   to unplug
 * registries for symbolic names of things
 * Factories which create objects from these symbolic names.
 * plus some utilities.

naming
In the presence of a module system oojs defines a module ; otherwise it creates a global   object. You just access, unlike passing   and   into an IIFE. You'll want to add  to your .jshintrc file.

Event emitter
In VE almost everything is an event emitter.

connect examples
OO.uiTool.js

this.toolbar.connect( this, { 'updateState': 'onUpdateState' } ); hooks you up to updateState events emitted by a toolbar.

Destruction
Call  from event emitter and you won't get any more events, also helps with GC (without something like this if your handler is bound to some event object then it won't be GC'd even when it goes out of scope).

You usually call disconnect in destroy method.

Registries and factories
Just a wrapper over a map, similar to

E.g. a registry of HTML tag names associated with a model, multiple tag names

Allows you to associate more than one key with the same value, e.g. aliasing.

A factory inherits from registry (using oojs inheritance).

When you register anything in a factory, the value must be a function, and the factory comes up with the key name for it.

See OO.Factory generated documentation

function Foo.static = { name: 'whatever' }

Our convention is lowerCamelCase.

Factory uses the symbolic name Projects tend to have multiple factories.

ooui has the notion of a tool factory, E.g. when you construct a toolbar you tell it what factory to get tools from. You only talk about toolbar components by their registry names.


 * First toolbar button created with name 'bold',


 * then if you want to have a different kind of bold button behavior, you can set a different tool.

Useful e.g. the 'link' button in VE has a simple implementation that inserts a plain URL, but mw-ve code supplies a different button class for the same registry name 'link' that can create a Page Name link.

ve.ui.LinkInspector.js function definition creates class (and the constructor), then specifies static.name = 'link';

Later on, ve.ui.inspectorFactory.register( ve.ui.LinkInspector );

A factory inherits from registry, inferring the name, and then knows how to create.

When someone clicks link (using the InspectorFactory) with 'link' in it, it gets the registered inspector for that from the factory.

But then in ve.ui.MWLinkInspector.js, it creates its own class, but by specifying the same static.name, any reference to that from the factory will now use this.

More on factory
Factory accepts any string for names.

ve adds a bunch more, e.g. it has ve. ve uses factories for things other than UI, e.g. a paragraph node is handed to a factory, not really about UI stuff.

Factory isn't for simple things like buttons, but information driving a UI where you can select the right UI configuration. They create an API. An example of creating a factory would be to create your own toolbar.

ooui
We've talked about the elements of oojs above because it's a relatively small library.

ooui is big and does a lot. We'll start by working through a simple example, and then a complex one.

Simple: creating a button
To create a new UI element, simply use the  operator with the element's class. As usual the plain config object is the first optional parameter; since the  class has no mandatory parameters, the call is just: The UI element contains a small unattached DOM fragment with the item's HTML. You can access this as a jQuery object as. Thus to insert this button in some existing HTML, you can use usual jQuery DOM manipulation methods, for example to insert this button after a set of items in a form layout:

Complex: displaying a modal dialog
Let's consider creating a dialog. The Flow extension displays comments on a topic, and there are buttons you can click to [Hide] and possibly [Delete] and [Suppress] a comment. When you click one of these buttons, a modal dialog appears with a text field in which you enter a reason why you Hid/Delete/Suppressed the comment.

Building the dialog
You create your own subclass of OO.ui.Dialog (see [https://doc.wikimedia.org/VisualEditor/master/#!/api/OO.ui.Dialog online documentation), say, and then let a dialog manager create instances of it as needed.

(It's similar to ve.ui.MWBetaWelcomeDialog.)

You "create" the class simply by defining your constructor object, by convention it starts with a capital letter.

config
(jsduck convention @cfg for the configuration object)

We saw in the simple example of creating a button that clients pass in a config object.

When implementing a class you can override the config object you're passed using  before calling parent object in order to get parents to do what you want. The convention is it's just static config values.

E.g. flow could extend the config object with.

then call parent constructor, which also takes the config. (and Dialog's parent, OO.ui.Window, also has config &mdash; it's config all the way down).

The convention is your required constructor parameters come first, then config object (if any), since config itself is optional.

One config thing really low-down is OO.ui.Element, the base class. It has its own config parameters, including  (to come)

Element
We saw in the simple example of creating a button that after creating an element it has a  property, which is the jQuery object representing its DOM fragment.

So after calling parent in your constructor, the object has a $ property (by default a ), and it's an unattached DOM element.

When someone uses your class
When some creates an instance of the class, the class's initialize gets called, and e.g. in initialize
 * compose the dialog from other things like layout, a [Continue] button, etc.

All dialogs have a head - body - foot.Foot is where you put buttons. These are jQuery things, so. It's a jQuery thing, not an OO.ui element.


 * Why a separate initialize?
 * With the exception of a Window, you can do anything you want in a constructor. initialize is separate because you have to wait for the iFrame to instantiate. More on lifecycle of objects later.

Want the text of the welcome message to be able to scroll, so it doesn't just $body.html( 'Some stuff here'), it creates a OO.ui.PanelLayout eventually putting that in the body with this.$body.append.

The [Save] button in the dialog
Add a [Save] Button. This is the same as the simple example, just  with the first and only constructor param being the config, where you say In order for the continueButton to to do something, you must handle click events, so using the oojs framework, call. Normally you'd just have a single string in here giving your 'onContinueClick' function name, not an array, and lower-down you would write. Because on close you specify an action signifying (to you, the writer of the dialog) whether to accept what happened, so you can decide whether to abort or toss.

Because you have to pass a parameter, can't just specify 'close'; the array is a shortcut for.

At bottom of class, stick the class in the dialog factory (see later).

Window lifecycle
Window has
 * construct (doesn't do much since have to wait for initialize )
 * initialize
 * each time the thing is used (i.e. before, can happen multiple times)
 * onSetup
 * then visible
 * onOpen
 * onClose
 * then invisible

They're pre-bound by parent class.

Actual object instantiation
When does this dialog appear? Most likely in response to some handler, e.g. the [Delete] button's click handler,

To make the dialog modal, you put it in an overlay div, and such. ooui is a robust system, so it solves this properly, at the cost of overhead.

windowSet
windowSet is a modal dialog manager, ensures only one is active at any time, handles the overlay.

You would have a global-ish mw.flow.windowSet created in UI setup and attach this to the DOM somewhere.

Assuming we have more than one kind of dialog, the same early-on initialization would instantiate a dialog factory.

mw.flow.dialogs = new OO.ui.DialogFactory;

no parameters.

This allows the dialog Class code at end to  mw.flow.dialogs.register( mw.flow.ReasonDialog );

is the thing that adds to the page.

back to the initiating button
The click handler for the button that pops up this dialog is just mw.flow.windowSet.open( 'reason', { 'type': 'hide', 'author': 'Roan' } )

where the last param is the config for the dialog. The window set will create the dialog that the factory maps to 'reason', and pass it a config object.

The way we to change the label for different reasons is in  swap out the label with setLabel

The dialog
The dialog has
 * TextInputWidget( { multiline: true } );
 * InputLabelWidget( label: 'Tell Roan why you are censoring him', input: myReasonTextInputWidget
 * a [Save] button

Responding to the Save button
To do some work, the idiom is 'close, 'apply'

In dialog's onClose if action is 'apply', actually do something, then call parent.close. E.g. MWReferenceDialog.js onDialog, Calling parent, actually have to know parent class to call it. OO.ui.Dialog.prototype.onClose.call( this, action ); N.B. could do OO.ui.Dialog.prototype.onClose.apply( this, arguments ); but often subclasses have different arguments.

And then look at 'action', and do the right thing.

mw.beta.WelcomeDialog had continue button panelLayout.$.append( object );

E.g. in onOpen, could focus the text input.

Smarter dialog handling
If button is conditional, see ReferenceDialog, basic concept is on 'change' event, check if TextInputWidget.getValue then this.submitButton.setDisabled( false );

'change' is smart, doesn't waste time until actual change.