OOUI

OOjs UI (Object-Oriented JavaScript – User Interface) is a modern JavaScript UI toolkit for browsers. It's a set of classes that you can use, that also let you compose more complex widgets.

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

MediaWiki client code using it will benefit from UX standardization.

Dependencies
OOjs UI is a separate project,. A gruntfile builds the JavaScript into a distributable library, for more details see the project README.md Its nifty interactive demo uses this library, and VisualEditor incorporates the library in extensions/VisualEditor/lib/ve/lib/oojs-ui. Eventually OOjs UI will be incorporated into MediaWiki core, as core jQuery is.

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

To use OOjs UI in a MediaWiki project:
 * in LocalSettings.php (until the module is in core).
 * Add  and   to the dependencies of your JavaScript ResourceLoader module(s).

Initially OOjs UI is a single ResourceLoader module, unlike the many RL modules of, say, jquery.ui

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

OOjs UI has its own default lightweight CSS style and small icon set. One day it will be "skinned" with the MediaWiki Agora styles.

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.


 * OOjs UI
 * 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 OOjs UI.).

OOjs

 * See generated documentation

OOjs provides
 * class inheritance via constructor and
 * mixin classes via constructor and
 * EventEmitter mixin (similar to Nodejs's emitter)
 * 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.
 * 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.

inheritance and mixin
OOjs provides both inheritance and mixin classes.

You call the constructors for both your parent and mixin classes in your own constructor. It's not automatic because constructors may take different parameters.

You then call  to set up the inheritance, and   to set up mixin classes.

Event emitter
In VE almost everything is an event emitter, so classes mix in

connect examples
OO.uiTool.js

this.toolbar.connect( this, { 'updateState': 'onUpdateState' } ); hooks your object up to updateState events emitted by a toolbar. This replaces declaring on handlers, and you can rig multiple ones in a single call.

Some base classes have event listeners that you can override... not common to invoke parent.

You can use an array to pass parameters along with the event. this.upButton.connect( this, { 'click': ['emit', 'move', -1] } ); that means that when clicked, the upButton will invoke this.emit( 'move', -1)

The method you call is typically in yourself, so you get object context.

Another example is the lookupMenu of search suggestions. You call its connect method and tell it to connect to in the context of something (usually yourself, but it could be something else), and then object is the event you're picking up.

is based on a more traditional event handler, there is one in OOjs called , similar to nodejs. this.lookupMenu.on( 'select', function { console.log( 'select called' ); } );

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.

The lower-level  function has a a corresponding   function.

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

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, using the class's  that you provide in its constructor. See OO.Factory generated documentation Our convention for  is lowerCamelCase.

This allows you to associate more than one key with the same value, e.g. aliasing, so code can ask for a 'help' pop-up without worrying about the implementing class. The

Projects tend to have multiple factories. OOjs UI 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.

Another example: the 'link' button in VE pops up a 'link' inspector that has a simple implementation that inserts a plain URL. The ve-mw code creates another  class using the same registry name 'link' that can insert a link to a Page Name.

In the ve.ui.LinkInspector.js class constructor function, after class

Later on, the class code registers it with VE's inspectorFactory: 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.

ve.ui.MWLinkInspector.js creates its own class, but by specifying the same static.name = 'link' and registering that with the inspectorFactory, any reference to  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.

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

OOjs UI 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  (used to be just .$). 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:

As a really basic way to tell the button worked, we can set up a 'click' event handler.

Elements and mixin classes
Nearly everything in OOjs UI is an element with a  – it's a piece of HTML.

But there are classes that you can mix in to other classes. These don't represent a piece of HTML but change the behavior of a class that does. E.g. OO.ui.ClippableElement will watch to see if something extends beyond screen boundary and if so will add a scrollbar. E.g. a menu toolgroup or list toolgroup will automatically gain a scrollbar if the window isn't tall enough to fit it, and will adjust as you scroll the window. (Google Docs, Mingle, etc. don't do this! You have to )

LabeledElement is another mixin. You pass it a config parameter of what the label text is and then it handles associating a label with an item. It provides a  representing the label, and then when you compose the item in a group such as a menu, the container lays out the item and its label.

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. This is a simple object containing static config values.

All constructors in OOjs UI take a config object as their first and only optional parameter; any required parameters come before it. When implementing a class you can override the config object you're passed using  before calling the parent constructor, in order to get parents to do what you want.

Our "reason" dialog could extend the config object with, and tell its parent it wants to be a small dialog with.

OO.ui.Element, the base class for most elements, has its own config parameters. These include, which lets you bind the element to a different jQuery context than the one in which you create it, see

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 this  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. OOjs UI 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.

this.$
jQuery's  is bound to the current document. With OOjs UI your components are often in an iframe, so the usual  will not find them – they are in a different document. So  provides   which is bound to the appropriate document.

Widgets
...
 * ButtonWidget - a button that supports 1 line of text
 * IconButtonWidget - a button with a label and an icon
 * PopupWidget - a flyout dialog box that is connected to a particular DOM element
 * PopupButtonWidget - an icon that when clicked opens up a flyout dialog.