/*!
- * OOjs UI v0.1.0-pre (7d3223b8f4)
+ * OOjs UI v0.1.0-pre (424b40373e)
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2014 OOjs Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: Thu Feb 13 2014 18:36:08 GMT-0800 (PST)
+ * Date: Fri Feb 14 2014 17:57:32 GMT-0800 (PST)
*/
( function () {
this.elementGroup = null;
// Initialization
- if ( Array.isArray( config.classes ) ) {
+ if ( $.isArray( config.classes ) ) {
this.$element.addClass( config.classes.join( ' ' ) );
}
if ( config.$content ) {
* @class
* @abstract
* @extends OO.ui.Element
- * @mixin OO.EventEmitter
+ * @mixins OO.EventEmitter
*
* @constructor
* @param {Object} [config] Configuration options
* @class
* @abstract
* @extends OO.ui.Element
- * @mixin OO.EventEmitter
+ * @mixins OO.EventEmitter
*
* @constructor
* @param {Object} [config] Configuration options
*
* @constructor
* @param {jQuery} $clippable Nodes to clip, assigned to #$clippable
+ * @param {Object} [config] Configuration options
*/
-OO.ui.ClippableElement = function OoUiClippableElement( $clippable ) {
+OO.ui.ClippableElement = function OoUiClippableElement( $clippable, config ) {
+ // Configuration initialization
+ config = config || {};
+
// Properties
this.$clippable = $clippable;
this.clipping = false;
var i, len, flag,
classPrefix = 'oo-ui-flaggableElement-';
- if ( Array.isArray( flags ) ) {
+ if ( $.isArray( flags ) ) {
for ( i = 0, len = flags.length; i < len; i++ ) {
flag = flags[i];
// Set
}
} else if ( OO.isPlainObject( flags ) ) {
for ( flag in flags ) {
- if ( flags[flags] ) {
+ if ( flags[flag] ) {
// Set
this.flags[flag] = true;
this.$element.addClass( classPrefix + flag );
item = items[i];
// Check if item exists then remove it first, effectively "moving" it
- currentIndex = this.items.indexOf( item );
+ currentIndex = $.inArray( item, this.items );
if ( currentIndex >= 0 ) {
this.removeItems( [ item ] );
// Adjust index to compensate for removal
// Remove specific items
for ( i = 0, len = items.length; i < len; i++ ) {
item = items[i];
- index = this.items.indexOf( item );
+ index = $.inArray( item, this.items );
if ( index !== -1 ) {
if ( this.aggregate ) {
item.disconnect( this );
/**
* Set the label.
*
+ * An empty string will result in the label being hidden. A string containing only whitespace will
+ * be converted to a single
+ *
* @method
* @param {jQuery|string|Function|null} label Label nodes; text; a function that retuns nodes or
* text; or null for no label
var empty = false;
this.label = label = OO.ui.resolveMsg( label ) || null;
- if ( typeof label === 'string' && label.trim() ) {
- this.$label.text( label );
+ if ( typeof label === 'string' && label.length ) {
+ if ( label.match( /^\s*$/ ) ) {
+ // Convert whitespace only string to a single non-breaking space
+ this.$label.html( ' ' );
+ } else {
+ this.$label.text( label );
+ }
} else if ( label instanceof jQuery ) {
this.$label.empty().append( label );
} else {
*
* @constructor
* @param {OO.Factory} toolFactory Factory for creating tools
- * @param {Object} [options] Configuration options
+ * @param {Object} [config] Configuration options
* @cfg {boolean} [actions] Add an actions section opposite to the tools
* @cfg {boolean} [shadow] Add a shadow below the toolbar
*/
-OO.ui.Toolbar = function OoUiToolbar( toolFactory, options ) {
+OO.ui.Toolbar = function OoUiToolbar( toolFactory, config ) {
// Configuration initialization
- options = options || {};
+ config = config || {};
// Parent constructor
- OO.ui.Element.call( this, options );
+ OO.ui.Element.call( this, config );
// Mixin constructors
OO.EventEmitter.call( this );
- OO.ui.GroupElement.call( this, this.$( '<div>' ) );
+ OO.ui.GroupElement.call( this, this.$( '<div>' ), config );
// Properties
this.toolFactory = toolFactory;
// Initialization
this.$group.addClass( 'oo-ui-toolbar-tools' );
this.$bar.addClass( 'oo-ui-toolbar-bar' ).append( this.$group );
- if ( options.actions ) {
+ if ( config.actions ) {
this.$actions.addClass( 'oo-ui-toolbar-actions' );
this.$bar.append( this.$actions );
}
this.$bar.append( '<div style="clear:both"></div>' );
- if ( options.shadow ) {
+ if ( config.shadow ) {
this.$bar.append( '<div class="oo-ui-toolbar-shadow"></div>' );
}
this.$element.addClass( 'oo-ui-toolbar' ).append( this.$bar );
}
}
}
- } else if ( Array.isArray( collection ) ) {
+ } else if ( $.isArray( collection ) ) {
for ( i = 0, len = collection.length; i < len; i++ ) {
item = collection[i];
// Allow plain strings as shorthand for named tools
OO.ui.Widget.call( this, config );
// Mixin constructors
- OO.ui.GroupElement.call( this, this.$( '<div>' ) );
+ OO.ui.GroupElement.call( this, this.$( '<div>' ), config );
// Properties
this.toolbar = toolbar;
/**
* Layout made of a fieldset and optional legend.
*
+ * Just add OO.ui.FieldLayout items.
+ *
* @class
* @extends OO.ui.Layout
* @mixins OO.ui.LabeledElement
+ * @mixins OO.ui.IconedElement
+ * @mixins OO.ui.GroupElement
*
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string} [icon] Symbolic icon name
+ * @cfg {OO.ui.FieldLayout[]} [items] Items to add
*/
OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
// Config initialization
OO.ui.Layout.call( this, config );
// Mixin constructors
+ OO.ui.IconedElement.call( this, this.$( '<div>' ), config );
OO.ui.LabeledElement.call( this, this.$( '<legend>' ), config );
+ OO.ui.GroupElement.call( this, this.$( '<div>' ), config );
// Initialization
- if ( config.icon ) {
- this.$element.addClass( 'oo-ui-fieldsetLayout-decorated' );
- this.$label.addClass( 'oo-ui-icon-' + config.icon );
- }
- this.$element.addClass( 'oo-ui-fieldsetLayout' );
- if ( config.icon || config.label ) {
- this.$element
- .addClass( 'oo-ui-fieldsetLayout-labeled' )
- .append( this.$label );
+ this.$element
+ .addClass( 'oo-ui-fieldsetLayout' )
+ .append( this.$icon, this.$label, this.$group );
+ if ( $.isArray( config.items ) ) {
+ this.addItems( config.items );
}
};
OO.inheritClass( OO.ui.FieldsetLayout, OO.ui.Layout );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.IconedElement );
OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.LabeledElement );
+OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.GroupElement );
/* Static Properties */
OO.ui.FieldsetLayout.static.tagName = 'fieldset';
+/**
+ * Layout made of a field and optional label.
+ *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.LabeledElement
+ *
+ * Available label alignment modes include:
+ * - 'left': Label is before the field and aligned away from it, best for when the user will be
+ * scanning for a specific label in a form with many fields
+ * - 'right': Label is before the field and aligned toward it, best for forms the user is very
+ * familiar with and will tab through field checking quickly to verify which field they are in
+ * - 'top': Label is before the field and above it, best for when the use will need to fill out all
+ * fields from top to bottom in a form with few fields
+ * - 'inline': Label is after the field and aligned toward it, best for small boolean fields like
+ * checkboxes or radio buttons
+ *
+ * @constructor
+ * @param {OO.ui.Widget} field Field widget
+ * @param {Object} [config] Configuration options
+ * @cfg {string} [align='left'] Alignment mode, either 'left', 'right', 'top' or 'inline'
+ */
+OO.ui.FieldLayout = function OoUiFieldLayout( field, config ) {
+ // Config initialization
+ config = $.extend( { 'align': 'left' }, config );
+
+ // Parent constructor
+ OO.ui.Layout.call( this, config );
+
+ // Mixin constructors
+ OO.ui.LabeledElement.call( this, this.$( '<label>' ), config );
+
+ // Properties
+ this.$field = this.$( '<div>' );
+ this.field = field;
+ this.align = null;
+
+ // Events
+ if ( this.field instanceof OO.ui.InputWidget ) {
+ this.$label.on( 'click', OO.ui.bind( this.onLabelClick, this ) );
+ }
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-fieldLayout' );
+ this.$field
+ .addClass( 'oo-ui-fieldLayout-field' )
+ .append( this.field.$element );
+ this.setAlignment( config.align );
+};
+
+/* Inheritance */
+
+OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
+
+OO.mixinClass( OO.ui.FieldLayout, OO.ui.LabeledElement );
+
+/* Methods */
+
+/**
+ * Handles label mouse click events.
+ *
+ * @method
+ * @param {jQuery.Event} e Mouse click event
+ */
+OO.ui.FieldLayout.prototype.onLabelClick = function () {
+ this.field.simulateLabelClick();
+ return false;
+};
+
+/**
+ * Get the field.
+ *
+ * @returns {OO.ui.Widget} Field widget
+ */
+OO.ui.FieldLayout.prototype.getField = function () {
+ return this.field;
+};
+
+/**
+ * Set the field alignment mode.
+ *
+ * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
+ * @chainable
+ */
+OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
+ if ( value !== this.align ) {
+ // Default to 'left'
+ if ( [ 'left', 'right', 'top', 'inline' ].indexOf( value ) === -1 ) {
+ value = 'left';
+ }
+ // Reorder elements
+ if ( value === 'inline' ) {
+ this.$element.append( this.$field, this.$label );
+ } else {
+ this.$element.append( this.$label, this.$field );
+ }
+ // Set classes
+ if ( this.align ) {
+ this.$element.removeClass( 'oo-ui-fieldLayout-align-' + this.align );
+ }
+ this.align = value;
+ this.$element.addClass( 'oo-ui-fieldLayout-align-' + this.align );
+ }
+
+ return this;
+};
/**
* Layout made of proportionally sized columns and rows.
*
* @chainable
*/
OO.ui.BookletLayout.prototype.addPages = function ( pages, index ) {
- var i, len, name, page,
+ var i, len, name, page, item,
items = [],
remove = [];
}
this.pages[page.getName()] = page;
if ( this.outlined ) {
- items.push( new OO.ui.BookletOutlineItemWidget( name, page, { '$': this.$ } ) );
+ item = new OO.ui.OutlineItemWidget( name, page, { '$': this.$ } );
+ page.setOutlineItem( item );
+ items.push( item );
}
}
if ( remove.length ) {
delete this.pages[name];
if ( this.outlined ) {
items.push( this.outlineWidget.getItemFromData( name ) );
+ page.setOutlineItem( null );
}
}
if ( this.outlined && items.length ) {
* @chainable
*/
OO.ui.BookletLayout.prototype.clearPages = function () {
- var pages = this.stackLayout.getItems();
+ var i, len,
+ pages = this.stackLayout.getItems();
this.pages = {};
this.currentPageName = null;
if ( this.outlined ) {
this.outlineWidget.clearItems();
+ for ( i = 0, len = pages.length; i < len; i++ ) {
+ pages[i].setOutlineItem( null );
+ }
}
this.stackLayout.clearItems();
* @constructor
* @param {string} name Unique symbolic name of page
* @param {Object} [config] Configuration options
- * @param {string} [icon=''] Symbolic name of icon to display in outline
- * @param {string} [indicator=''] Symbolic name of indicator to display in outline
- * @param {string} [indicatorTitle=''] Description of indicator meaning to display in outline
- * @param {string} [label=''] Label to display in outline
- * @param {number} [level=0] Indentation level of item in outline
- * @param {boolean} [movable=false] Page should be movable using outline controls
+ * @param {string} [outlineItem] Outline item widget
*/
OO.ui.PageLayout = function OoUiPageLayout( name, config ) {
// Configuration initialization
// Properties
this.name = name;
- this.icon = config.icon || '';
- this.indicator = config.indicator || '';
- this.indicatorTitle = OO.ui.resolveMsg( config.indicatorTitle ) || '';
- this.label = OO.ui.resolveMsg( config.label ) || '';
- this.level = config.level || 0;
- this.movable = !!config.movable;
+ this.outlineItem = config.outlineItem || null;
// Initialization
this.$element.addClass( 'oo-ui-pageLayout' );
};
/**
- * Get page icon.
- *
- * @returns {string} Symbolic name of icon
- */
-OO.ui.PageLayout.prototype.getIcon = function () {
- return this.icon;
-};
-
-/**
- * Get page indicator.
- *
- * @returns {string} Symbolic name of indicator
- */
-OO.ui.PageLayout.prototype.getIndicator = function () {
- return this.indicator;
-};
-
-/**
- * Get page indicator label.
- *
- * @returns {string} Description of indicator meaning
- */
-OO.ui.PageLayout.prototype.getIndicatorTitle = function () {
- return this.indicatorTitle;
-};
-
-/**
- * Get page label.
+ * Get outline item.
*
- * @returns {string} Label text
+ * @returns {OO.ui.OutlineItemWidget|null} Outline item widget
*/
-OO.ui.PageLayout.prototype.getLabel = function () {
- return this.label;
+OO.ui.PageLayout.prototype.getOutlineItem = function () {
+ return this.outlineItem;
};
/**
- * Get outline item indentation level.
+ * Get outline item.
*
- * @returns {number} Indentation level
- */
-OO.ui.PageLayout.prototype.getLevel = function () {
- return this.level;
-};
-
-/**
- * Check if page is movable using outline controls.
- *
- * @returns {boolean} Page is movable
+ * @param {OO.ui.OutlineItemWidget|null} outlineItem Outline item widget, null to clear
+ * @chainable
*/
-OO.ui.PageLayout.prototype.isMovable = function () {
- return this.movable;
+OO.ui.PageLayout.prototype.setOutlineItem = function ( outlineItem ) {
+ this.outlineItem = outlineItem;
+ return this;
};
/**
* Layout containing a series of mutually exclusive pages.
* @param {Object} [config] Configuration options
* @cfg {boolean} [continuous=false] Show all pages, one after another
* @cfg {string} [icon=''] Symbolic icon name
+ * @cfg {OO.ui.Layout[]} [items] Layouts to add
*/
OO.ui.StackLayout = function OoUiStackLayout( config ) {
// Config initialization
if ( this.continuous ) {
this.$element.addClass( 'oo-ui-stackLayout-continuous' );
}
+ if ( $.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
};
/* Inheritance */
*/
OO.ui.StackLayout.prototype.removeItems = function ( items ) {
OO.ui.GroupElement.prototype.removeItems.call( this, items );
- if ( items.indexOf( this.currentItem ) !== -1 ) {
+ if ( $.inArray( this.currentItem, items ) !== -1 ) {
this.currentItem = null;
if ( !this.currentItem && this.items.length ) {
this.setItem( this.items[0] );
if ( !this.continuous ) {
this.$items.css( 'display', '' );
}
- if ( this.items.indexOf( item ) !== -1 ) {
+ if ( $.inArray( item, this.items ) !== -1 ) {
if ( !this.continuous ) {
item.$element.css( 'display', 'block' );
}
OO.ui.IndicatedElement.call( this, this.$( '<span>' ), config );
OO.ui.LabeledElement.call( this, this.$( '<span>' ), config );
OO.ui.TitledElement.call( this, this.$element, config );
- OO.ui.ClippableElement.call( this, this.$group );
+ OO.ui.ClippableElement.call( this, this.$group, config );
// Properties
this.active = false;
*
* @class
* @extends OO.ui.Widget
- * @mixin OO.ui.GroupElement
+ * @mixins OO.ui.GroupElement
*
* @constructor
* @param {Object} [config] Configuration options
+ * @cfg {OO.ui.ButtonWidget} [items] Buttons to add
*/
OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
// Parent constructor
// Initialization
this.$element.addClass( 'oo-ui-buttonGroupWidget' );
+ if ( $.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
};
/* Inheritance */
}
};
+/**
+ * Simulate the behavior of clicking on a label bound to this input.
+ *
+ * @method
+ */
+OO.ui.InputWidget.prototype.simulateLabelClick = function () {
+ if ( !this.isDisabled() ) {
+ if ( this.$input.is( ':checkbox,:radio' ) ) {
+ this.$input.click();
+ } else if ( this.$input.is( ':input' ) ) {
+ this.$input.focus();
+ }
+ }
+};
+
/**
* Check if the widget is read-only.
*
this.$input.prop( 'disabled', this.disabled );
}
return this;
-};/**
+};
+/**
* Creates an OO.ui.CheckboxInputWidget object.
*
* @class
}
};
/**
- * Creates an OO.ui.CheckboxWidget object.
- *
- * @class
- * @extends OO.ui.CheckboxInputWidget
- * @mixins OO.ui.LabeledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [label=''] Label
- */
-OO.ui.CheckboxWidget = function OoUiCheckboxWidget( config ) {
- // Configuration initialization
- config = config || {};
-
- // Parent constructor
- OO.ui.CheckboxInputWidget.call( this, config );
-
- // Mixin constructors
- OO.ui.LabeledElement.call( this, this.$( '<span>' ) , config );
-
- // Initialization
- this.$element
- .addClass( 'oo-ui-checkboxWidget' )
- .append( this.$( '<label>' ).append( this.$input, this.$label ) );
-};
-
-/* Inheritance */
-
-OO.inheritClass( OO.ui.CheckboxWidget, OO.ui.CheckboxInputWidget );
-
-OO.mixinClass( OO.ui.CheckboxWidget, OO.ui.LabeledElement );
-/**
- * Creates an OO.ui.InputLabelWidget object.
- *
- * CSS classes will be added to the button for each flag, each prefixed with 'oo-ui-InputLabelWidget-'
+ * Creates an OO.ui.LabelWidget object.
*
* @class
* @extends OO.ui.Widget
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {OO.ui.InputWidget|null} [input] Related input widget
*/
-OO.ui.InputLabelWidget = function OoUiInputLabelWidget( config ) {
+OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
// Config intialization
- config = $.extend( { 'input': null }, config );
+ config = config || {};
// Parent constructor
OO.ui.Widget.call( this, config );
this.input = config.input;
// Events
- this.$element.on( 'click', OO.ui.bind( this.onClick, this ) );
+ if ( this.input instanceof OO.ui.InputWidget ) {
+ this.$element.on( 'click', OO.ui.bind( this.onClick, this ) );
+ }
// Initialization
- this.$element.addClass( 'oo-ui-inputLabelWidget' );
+ this.$element.addClass( 'oo-ui-labelWidget' );
};
/* Inheritance */
-OO.inheritClass( OO.ui.InputLabelWidget, OO.ui.Widget );
+OO.inheritClass( OO.ui.LabelWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.InputLabelWidget, OO.ui.LabeledElement );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.LabeledElement );
/* Static Properties */
-OO.ui.InputLabelWidget.static.tagName = 'label';
+OO.ui.LabelWidget.static.tagName = 'label';
/* Methods */
/**
- * Handles mouse click events.
+ * Handles label mouse click events.
*
* @method
* @param {jQuery.Event} e Mouse click event
*/
-OO.ui.InputLabelWidget.prototype.onClick = function () {
- if ( !this.disabled && this.input ) {
- this.input.$input.focus();
- }
+OO.ui.LabelWidget.prototype.onClick = function () {
+ this.input.simulateLabelClick();
return false;
};
/**
* @mixins OO.ui.IconedElement
* @mixins OO.ui.LabeledElement
* @mixins OO.ui.IndicatedElement
+ * @mixins OO.ui.FlaggableElement
*
* @constructor
* @param {Mixed} data Option data
OO.ui.IconedElement.call( this, this.$( '<span>' ), config );
OO.ui.LabeledElement.call( this, this.$( '<span>' ), config );
OO.ui.IndicatedElement.call( this, this.$( '<span>' ), config );
+ OO.ui.FlaggableElement.call( this, config );
// Properties
this.data = data;
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IconedElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.LabeledElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.IndicatedElement );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.FlaggableElement );
/* Static Properties */
* @class
* @abstract
* @extends OO.ui.Widget
- * @mixin OO.ui.GroupElement
+ * @mixins OO.ui.GroupElement
*
* @constructor
* @param {Object} [config] Configuration options
+ * @cfg {OO.ui.OptionWidget[]} [items] Options to add
*/
OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
// Config intialization
// Initialization
this.$element.addClass( 'oo-ui-selectWidget' );
+ if ( $.isArray( config.items ) ) {
+ this.addItems( config.items );
+ }
};
/* Inheritance */
var inc = direction > 0 ? 1 : -1,
len = this.items.length,
index = item instanceof OO.ui.OptionWidget ?
- this.items.indexOf( item ) : ( inc > 0 ? -1 : 0 ),
+ $.inArray( item, this.items ) : ( inc > 0 ? -1 : 0 ),
stopAt = Math.max( Math.min( index, len - 1 ), 0 ),
i = inc > 0 ?
// Default to 0 instead of -1, if nothing is selected let's start at the beginning
OO.ui.SelectWidget.call( this, config );
// Mixin constructors
- OO.ui.ClippableElement.call( this, this.$group );
+ OO.ui.ClippableElement.call( this, this.$group, config );
// Properties
this.newItems = [];
/**
* Check if item is movable.
*
- * Moveablilty is used by outline controls.
+ * Movablilty is used by outline controls.
*
* @returns {boolean} Item is movable
*/
return this.level;
};
+/**
+ * Set movability.
+ *
+ * Movablilty is used by outline controls.
+ *
+ * @param {boolean} movable Item is movable
+ * @chainable
+ */
+OO.ui.OutlineItemWidget.prototype.setMovable = function ( movable ) {
+ this.movable = !!movable;
+ return this;
+};
+
/**
* Set indentation level.
*
return this;
};
-/**
- * Creates an OO.ui.BookletOutlineItemWidget object.
- *
- * @class
- * @extends OO.ui.OutlineItemWidget
- *
- * @constructor
- * @param {Mixed} data Item data
- * @param {Object} [config] Configuration options
- */
-OO.ui.BookletOutlineItemWidget = function OoUiBookletOutlineItemWidget( data, page, config ) {
- // Configuration intialization
- config = $.extend( {
- 'label': page.getLabel() || data,
- 'level': page.getLevel(),
- 'icon': page.getIcon(),
- 'indicator': page.getIndicator(),
- 'indicatorTitle': page.getIndicatorTitle(),
- 'movable': page.isMovable()
- }, config );
-
- // Parent constructor
- OO.ui.OutlineItemWidget.call( this, data, config );
-
- // Initialization
- this.$element.addClass( 'oo-ui-bookletOutlineItemWidget' );
-};
-
-/* Inheritance */
-
-OO.inheritClass( OO.ui.BookletOutlineItemWidget, OO.ui.OutlineItemWidget );
/**
* Create an OO.ui.ButtonSelect object.
*