OOUI

ooui is a modern UI toolkit for browsers.

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

How
ooui will be a distribution, like Mediawiki core.

Somehow it will be incorporated, like jQuery

For a developer, just depend on oo-ui in ResourceLoader.

Initially just one module, unlike jquery.ui variations

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

Dependencies
Assumes jQuery It doesn't assume MediaWiki, not even MediaWiki localization.

oojs
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 names of events to your on handlers, also binding  correctly. Nice shortcut, and there's a   to unplug
 * registries for symbolic names of
 * plus some utilities.

Event emitter
In VE almost everything is an event emitter.

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.

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 jsduck documentation

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

Our convention is lowerCamelCase.

Factory uses the symbolic name It

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 (plain URL inserter), but mw-ve code supplies a different button class for the same registry 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.

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.

ooui
Example of creating a factory would be to create your own toolbar.

But let's consider creating a dialog.

Example runthrough

 * 1) Create your own subclass of OO.ui.Dialog (see jsduck)  (was ve.ui.Dialog)
 * 2) * mw.flow.ui.ReasonDialog

e.g. ve.ui.BetaWelcomeDialog

define your constructor, in this case, override config using $.extend (ve aliases things, but not required)

config
jsduck convention @cfg for the configuration object.

You modify config to get parents do what you want. Convention is it's just static config values.

E.g. flow could have flowReasonType: 'suppress' in config.

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).

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
Element has a .$ property, which is the jQuery/DOM thing you get.

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

When returned to your caller, the caller can .attach this .$ thing.

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.

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.

Add a continueButton, simply new OO.ui.PushButtonWidget, first and only constructor param is the config, where you say  and

continueButton has to do something, so you just. 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).

More on dialog setup
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
Responding to some handler, e.g. the Hide button handler,

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

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 window set, still in initialization setup
mw.flow.windowSet = new OO.ui.WindowSet( mw.flow.dialogs ) $( 'body' ).append( windowSet.$ );

back to the button
Hide button click handler, just mw.flow.windowSet.open( 'reason' { 'type': 'suppress', 'author': 'Roan' } )

where the second param is the config.

The way we could swap this out for the reason, is in  swap out the label with setLabel

The rest of the thing
TextInputWidget( { multiline: true } ); InputLabelWidget( label: 'Tell Roan why you are censoring him', input: myReasonTextInputWidget

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.

Examples
OO.uiTool.js

this.toolbar.connect( this, { 'updateState': 'onUpdateState' } );

Hooks you up to updateState events from a toolbar.

Destruction
Call disconnect from event emitter and you won't get any more events, also helps with GC (alternative if you're bound to some event object won't be GC'd even when it goes out of scope). Do this in destroy.