aboutsummaryrefslogtreecommitdiffstats
path: root/resources/lib/oojs-ui/oojs-ui.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/lib/oojs-ui/oojs-ui.js')
-rw-r--r--resources/lib/oojs-ui/oojs-ui.js227
1 files changed, 161 insertions, 66 deletions
diff --git a/resources/lib/oojs-ui/oojs-ui.js b/resources/lib/oojs-ui/oojs-ui.js
index ff1163117919..d965ffa7ade8 100644
--- a/resources/lib/oojs-ui/oojs-ui.js
+++ b/resources/lib/oojs-ui/oojs-ui.js
@@ -1,12 +1,12 @@
/*!
- * OOjs UI v0.1.0-pre (0a7180f468)
+ * OOjs UI v0.1.0-pre (70f1886a35)
* 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: Wed Apr 23 2014 18:05:30 GMT-0700 (PDT)
+ * Date: Tue Apr 29 2014 17:13:10 GMT-0700 (PDT)
*/
( function ( OO ) {
@@ -173,7 +173,8 @@ OO.ui.resolveMsg = function ( msg ) {
* @param {Object} [config] Configuration options
* @cfg {Function} [$] jQuery for the frame the widget is in
* @cfg {string[]} [classes] CSS class names
- * @cfg {jQuery} [$content] Content elements to append
+ * @cfg {string} [text] Text to insert
+ * @cfg {jQuery} [$content] Content elements to append (after text)
*/
OO.ui.Element = function OoUiElement( config ) {
// Configuration initialization
@@ -188,6 +189,9 @@ OO.ui.Element = function OoUiElement( config ) {
if ( $.isArray( config.classes ) ) {
this.$element.addClass( config.classes.join( ' ' ) );
}
+ if ( config.text ) {
+ this.$element.text( config.text );
+ }
if ( config.$content ) {
this.$element.append( config.$content );
}
@@ -612,32 +616,52 @@ OO.ui.Element.prototype.offDOMEvent = function ( event, callback ) {
( function () {
// Static
- var specialFocusin;
- function handler( e ) {
- jQuery.event.simulate( 'focusin', e.target, jQuery.event.fix( e ), /* bubble = */ true );
- }
+ // jQuery 1.8.3 has a bug with handling focusin/focusout events inside iframes.
+ // Firefox doesn't support focusin/focusout at all, so we listen for 'focus'/'blur' on the
+ // document, and simulate a 'focusin'/'focusout' event on the target element and make
+ // it bubble from there.
+ //
+ // - http://jsfiddle.net/sw3hr/
+ // - http://bugs.jquery.com/ticket/14180
+ // - https://github.com/jquery/jquery/commit/1cecf64e5aa4153
+ function specialEvent( simulatedName, realName ) {
+ function handler( e ) {
+ jQuery.event.simulate(
+ simulatedName,
+ e.target,
+ jQuery.event.fix( e ),
+ /* bubble = */ true
+ );
+ }
- specialFocusin = {
- setup: function () {
- var doc = this.ownerDocument || this,
- attaches = $.data( doc, 'ooui-focusin-attaches' );
- if ( !attaches ) {
- doc.addEventListener( 'focus', handler, true );
- }
- $.data( doc, 'ooui-focusin-attaches', ( attaches || 0 ) + 1 );
- },
- teardown: function () {
- var doc = this.ownerDocument || this,
- attaches = $.data( doc, 'ooui-focusin-attaches' ) - 1;
- if ( !attaches ) {
- doc.removeEventListener( 'focus', handler, true );
- $.removeData( doc, 'ooui-focusin-attaches' );
- } else {
- $.data( doc, 'ooui-focusin-attaches', attaches );
+ return {
+ setup: function () {
+ var doc = this.ownerDocument || this,
+ attaches = $.data( doc, 'ooui-' + simulatedName + '-attaches' );
+ if ( !attaches ) {
+ doc.addEventListener( realName, handler, true );
+ }
+ $.data( doc, 'ooui-' + simulatedName + '-attaches', ( attaches || 0 ) + 1 );
+ },
+ teardown: function () {
+ var doc = this.ownerDocument || this,
+ attaches = $.data( doc, 'ooui-' + simulatedName + '-attaches' ) - 1;
+ if ( !attaches ) {
+ doc.removeEventListener( realName, handler, true );
+ $.removeData( doc, 'ooui-' + simulatedName + '-attaches' );
+ } else {
+ $.data( doc, 'ooui-' + simulatedName + '-attaches', attaches );
+ }
}
- }
- };
+ };
+ }
+
+ var hasOwn = Object.prototype.hasOwnProperty,
+ specialEvents = {
+ focusin: specialEvent( 'focusin', 'focus' ),
+ focusout: specialEvent( 'focusout', 'blur' )
+ };
/**
* Bind a handler for an event on a DOM element.
@@ -654,25 +678,15 @@ OO.ui.Element.prototype.offDOMEvent = function ( event, callback ) {
OO.ui.Element.onDOMEvent = function ( el, event, callback ) {
var orig;
- if ( event === 'focusin' ) {
- // jQuery 1.8.3 has a bug with handling focusin events inside iframes.
- // Firefox doesn't support focusin at all, so we listen for 'focus' on the
- // document, and simulate a 'focusin' event on the target element and make
- // it bubble from there.
- //
- // - http://jsfiddle.net/sw3hr/
- // - http://bugs.jquery.com/ticket/14180
- // - https://github.com/jquery/jquery/commit/1cecf64e5aa4153
-
+ if ( hasOwn.call( specialEvents, event ) ) {
// Replace jQuery's override with our own
- orig = $.event.special.focusin;
- $.event.special.focusin = specialFocusin;
+ orig = $.event.special[event];
+ $.event.special[event] = specialEvents[event];
$( el ).on( event, callback );
// Restore
- $.event.special.focusin = orig;
-
+ $.event.special[event] = orig;
} else {
$( el ).on( event, callback );
}
@@ -688,11 +702,15 @@ OO.ui.Element.prototype.offDOMEvent = function ( event, callback ) {
*/
OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
var orig;
- if ( event === 'focusin' ) {
- orig = $.event.special.focusin;
- $.event.special.focusin = specialFocusin;
+ if ( hasOwn.call( specialEvents, event ) ) {
+ // Replace jQuery's override with our own
+ orig = $.event.special[event];
+ $.event.special[event] = specialEvents[event];
+
$( el ).off( event, callback );
- $.event.special.focusin = orig;
+
+ // Restore
+ $.event.special[event] = orig;
} else {
$( el ).off( event, callback );
}
@@ -1279,7 +1297,9 @@ OO.ui.Window.prototype.teardown = function () {
* Do not override this method. See #setup for a way to make changes each time the window opens.
*
* @param {Object} [data] Window opening data
+ * @fires opening
* @fires open
+ * @fires ready
* @chainable
*/
OO.ui.Window.prototype.open = function ( data ) {
@@ -1290,13 +1310,16 @@ OO.ui.Window.prototype.open = function ( data ) {
this.visible = true;
this.emit( 'opening', data );
this.setup( data );
- // Focus the content div (which has a tabIndex) to inactivate
- // (but not clear) selections in the parent frame.
- // Must happen after setup runs (otherwise focusing it doesn't work)
- // but before 'open' is emitted (so subclasses can give focus to something else)
- this.frame.$content.focus();
this.emit( 'open', data );
- this.opening = false;
+ setTimeout( OO.ui.bind( function () {
+ // Focus the content div (which has a tabIndex) to inactivate
+ // (but not clear) selections in the parent frame.
+ // Must happen after 'open' is emitted (to ensure it is visible)
+ // but before 'ready' is emitted (so subclasses can give focus to something else)
+ this.frame.$content.focus();
+ this.emit( 'ready', data );
+ this.opening = false;
+ }, this ) );
}, this ) );
}
@@ -1309,6 +1332,7 @@ OO.ui.Window.prototype.open = function ( data ) {
* See #teardown for a way to do something each time the window closes.
*
* @param {Object} [data] Window closing data
+ * @fires closing
* @fires close
* @chainable
*/
@@ -2206,7 +2230,6 @@ OO.ui.FlaggableElement.prototype.setFlags = function ( flags ) {
* @constructor
* @param {jQuery} $group Container node, assigned to #$group
* @param {Object} [config] Configuration options
- * @cfg {Object.<string,string>} [aggregations] Events to aggregate, keyed by item event name
*/
OO.ui.GroupElement = function OoUiGroupElement( $group, config ) {
// Configuration
@@ -2216,8 +2239,7 @@ OO.ui.GroupElement = function OoUiGroupElement( $group, config ) {
this.$group = $group;
this.items = [];
this.$items = this.$( [] );
- this.aggregate = !$.isEmptyObject( config.aggregations );
- this.aggregations = config.aggregations || {};
+ this.aggregateItemEvents = {};
};
/* Methods */
@@ -2232,6 +2254,59 @@ OO.ui.GroupElement.prototype.getItems = function () {
};
/**
+ * Add an aggregate item event.
+ *
+ * Aggregated events are listened to on each item and then emitted by the group under a new name,
+ * and with an additional leading parameter containing the item that emitted the original event.
+ * Other arguments that were emitted from the original event are passed through.
+ *
+ * @param {Object.<string,string|null>} events Aggregate events emitted by group, keyed by item
+ * event, use null value to remove aggregation
+ * @throws {Error} If aggregation already exists
+ */
+OO.ui.GroupElement.prototype.aggregate = function ( events ) {
+ var i, len, item, add, remove, itemEvent, groupEvent;
+
+ for ( itemEvent in events ) {
+ groupEvent = events[itemEvent];
+
+ // Remove existing aggregated event
+ if ( itemEvent in this.aggregateItemEvents ) {
+ // Don't allow duplicate aggregations
+ if ( groupEvent ) {
+ throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
+ }
+ // Remove event aggregation from existing items
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[i];
+ if ( item.connect && item.disconnect ) {
+ remove = {};
+ remove[itemEvent] = [ 'emit', groupEvent, item ];
+ item.disconnect( this, remove );
+ }
+ }
+ // Prevent future items from aggregating event
+ delete this.aggregateItemEvents[itemEvent];
+ }
+
+ // Add new aggregate event
+ if ( groupEvent ) {
+ // Make future items aggregate event
+ this.aggregateItemEvents[itemEvent] = groupEvent;
+ // Add event aggregation to existing items
+ for ( i = 0, len = this.items.length; i < len; i++ ) {
+ item = this.items[i];
+ if ( item.connect && item.disconnect ) {
+ add = {};
+ add[itemEvent] = [ 'emit', groupEvent, item ];
+ item.connect( this, add );
+ }
+ }
+ }
+ }
+};
+
+/**
* Add items.
*
* @param {OO.ui.Element[]} items Item
@@ -2255,10 +2330,10 @@ OO.ui.GroupElement.prototype.addItems = function ( items, index ) {
}
}
// Add the item
- if ( this.aggregate ) {
+ if ( item.connect && item.disconnect && !$.isEmptyObject( this.aggregateItemEvents ) ) {
events = {};
- for ( event in this.aggregations ) {
- events[event] = [ 'emit', this.aggregations[event], item ];
+ for ( event in this.aggregateItemEvents ) {
+ events[event] = [ 'emit', this.aggregateItemEvents[event], item ];
}
item.connect( this, events );
}
@@ -2291,15 +2366,22 @@ OO.ui.GroupElement.prototype.addItems = function ( items, index ) {
* @chainable
*/
OO.ui.GroupElement.prototype.removeItems = function ( items ) {
- var i, len, item, index;
+ var i, len, item, index, remove, itemEvent;
// Remove specific items
for ( i = 0, len = items.length; i < len; i++ ) {
item = items[i];
index = $.inArray( item, this.items );
if ( index !== -1 ) {
- if ( this.aggregate ) {
- item.disconnect( this );
+ if (
+ item.connect && item.disconnect &&
+ !$.isEmptyObject( this.aggregateItemEvents )
+ ) {
+ remove = {};
+ if ( itemEvent in this.aggregateItemEvents ) {
+ remove[itemEvent] = [ 'emit', this.aggregateItemEvents[itemEvent], item ];
+ }
+ item.disconnect( this, remove );
}
item.setElementGroup( null );
this.items.splice( index, 1 );
@@ -2319,13 +2401,20 @@ OO.ui.GroupElement.prototype.removeItems = function ( items ) {
* @chainable
*/
OO.ui.GroupElement.prototype.clearItems = function () {
- var i, len, item;
+ var i, len, item, remove, itemEvent;
// Remove all items
for ( i = 0, len = this.items.length; i < len; i++ ) {
item = this.items[i];
- if ( this.aggregate ) {
- item.disconnect( this );
+ if (
+ item.connect && item.disconnect &&
+ !$.isEmptyObject( this.aggregateItemEvents )
+ ) {
+ remove = {};
+ if ( itemEvent in this.aggregateItemEvents ) {
+ remove[itemEvent] = [ 'emit', this.aggregateItemEvents[itemEvent], item ];
+ }
+ item.disconnect( this, remove );
}
item.setElementGroup( null );
}
@@ -3344,9 +3433,7 @@ OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
*/
OO.ui.ToolGroup = function OoUiToolGroup( toolbar, config ) {
// Configuration initialization
- config = $.extend( true, {
- 'aggregations': { 'disable': 'itemDisable' }
- }, config );
+ config = config || {};
// Parent constructor
OO.ui.ToolGroup.super.call( this, config );
@@ -3373,6 +3460,7 @@ OO.ui.ToolGroup = function OoUiToolGroup( toolbar, config ) {
'mouseout': OO.ui.bind( this.onMouseOut, this )
} );
this.toolbar.getToolFactory().connect( this, { 'register': 'onToolFactoryRegister' } );
+ this.aggregate( { 'disable': 'itemDisable' } );
this.connect( this, { 'itemDisable': 'updateDisabled' } );
// Initialization
@@ -5464,7 +5552,7 @@ OO.ui.InputWidget.prototype.isReadOnly = function () {
*/
OO.ui.InputWidget.prototype.setReadOnly = function ( state ) {
this.readOnly = !!state;
- this.$input.prop( 'readonly', this.readOnly );
+ this.$input.prop( 'readOnly', this.readOnly );
return this;
};
@@ -7707,6 +7795,13 @@ OO.ui.SearchWidget.prototype.clear = function () {
};
/**
+ * Focus the query input.
+ */
+OO.ui.SearchWidget.prototype.focus = function () {
+ this.query.$input[0].focus();
+};
+
+/**
* Get the results list.
*
* @return {OO.ui.SelectWidget} Select list