X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=blobdiff_plain;f=resources%2Flib%2Foojs-ui%2Foojs-ui-core.js;h=1b90db575ccd4665f28ff40945b58f53e40e9142;hb=da0b4741984825903748e99980d5b86478cd933d;hp=e244037302d7141ce6b365289e71e09742285755;hpb=8515a2e32d0bb168ef1fb0583d388f83217d155a;p=lhc%2Fweb%2Fwiklou.git
diff --git a/resources/lib/oojs-ui/oojs-ui-core.js b/resources/lib/oojs-ui/oojs-ui-core.js
index e244037302..1b90db575c 100644
--- a/resources/lib/oojs-ui/oojs-ui-core.js
+++ b/resources/lib/oojs-ui/oojs-ui-core.js
@@ -1,12 +1,12 @@
/*!
- * OOjs UI v0.16.0
+ * OOjs UI v0.16.3
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011â2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-02-22T22:33:33Z
+ * Date: 2016-03-16T19:20:22Z
*/
( function ( OO ) {
@@ -56,14 +56,14 @@ OO.ui.MouseButtons = {
};
/**
- * @property {Number}
+ * @property {number}
*/
OO.ui.elementId = 0;
/**
* Generate a unique ID for element
*
- * @return {String} [id]
+ * @return {string} [id]
*/
OO.ui.generateElementId = function () {
OO.ui.elementId += 1;
@@ -74,7 +74,7 @@ OO.ui.generateElementId = function () {
* Check if an element is focusable.
* Inspired from :focusable in jQueryUI v1.11.4 - 2015-04-14
*
- * @param {jQuery} element Element to test
+ * @param {jQuery} $element Element to test
* @return {boolean}
*/
OO.ui.isFocusableElement = function ( $element ) {
@@ -260,6 +260,58 @@ OO.ui.debounce = function ( func, wait, immediate ) {
};
};
+/**
+ * Returns a function, that, when invoked, will only be triggered at most once
+ * during a given window of time. If called again during that window, it will
+ * wait until the window ends and then trigger itself again.
+ *
+ * As it's not knowable to the caller whether the function will actually run
+ * when the wrapper is called, return values from the function are entirely
+ * discarded.
+ *
+ * @param {Function} func
+ * @param {number} wait
+ * @return {Function}
+ */
+OO.ui.throttle = function ( func, wait ) {
+ var context, args, timeout,
+ previous = 0,
+ run = function () {
+ timeout = null;
+ previous = OO.ui.now();
+ func.apply( context, args );
+ };
+ return function () {
+ // Check how long it's been since the last time the function was
+ // called, and whether it's more or less than the requested throttle
+ // period. If it's less, run the function immediately. If it's more,
+ // set a timeout for the remaining time -- but don't replace an
+ // existing timeout, since that'd indefinitely prolong the wait.
+ var remaining = wait - ( OO.ui.now() - previous );
+ context = this;
+ args = arguments;
+ if ( remaining <= 0 ) {
+ // Note: unless wait was ridiculously large, this means we'll
+ // automatically run the first time the function was called in a
+ // given period. (If you provide a wait period larger than the
+ // current Unix timestamp, you *deserve* unexpected behavior.)
+ clearTimeout( timeout );
+ run();
+ } else if ( !timeout ) {
+ timeout = setTimeout( run, remaining );
+ }
+ };
+};
+
+/**
+ * A (possibly faster) way to get the current timestamp as an integer
+ *
+ * @return {number} Current timestamp
+ */
+OO.ui.now = Date.now || function () {
+ return new Date().getTime();
+};
+
/**
* Proxy for `node.addEventListener( eventName, handler, true )`.
*
@@ -356,7 +408,7 @@ OO.ui.infuse = function ( idOrNode ) {
* they support unnamed, ordered message parameters.
*
* @param {string} key Message key
- * @param {Mixed...} [params] Message parameters
+ * @param {...Mixed} [params] Message parameters
* @return {string} Translated message with parameters substituted
*/
OO.ui.msg = function ( key ) {
@@ -382,7 +434,7 @@ OO.ui.infuse = function ( idOrNode ) {
* Use this when you are statically specifying a message and the message may not yet be present.
*
* @param {string} key Message key
- * @param {Mixed...} [params] Message parameters
+ * @param {...Mixed} [params] Message parameters
* @return {Function} Function that returns the resolved message when executed
*/
OO.ui.deferMsg = function () {
@@ -579,6 +631,7 @@ OO.ui.Element.static.infuse = function ( idOrNode ) {
/**
* Implementation helper for `infuse`; skips the type check and has an
* extra property so that only the top-level invocation touches the DOM.
+ *
* @private
* @param {string|HTMLElement|jQuery} idOrNode
* @param {jQuery.Promise|boolean} domPromise A promise that will be resolved
@@ -610,13 +663,13 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
}
if ( domPromise ) {
// pick up dynamic state, like focus, value of form inputs, scroll position, etc.
- state = data.gatherPreInfuseState( $elem );
+ state = data.constructor.static.gatherPreInfuseState( $elem, data );
// restore dynamic state after the new element is re-inserted into DOM under infused parent
domPromise.done( data.restorePreInfuseState.bind( data, state ) );
infusedChildren = $elem.data( 'ooui-infused-children' );
if ( infusedChildren && infusedChildren.length ) {
infusedChildren.forEach( function ( data ) {
- var state = data.gatherPreInfuseState( $elem );
+ var state = data.constructor.static.gatherPreInfuseState( $elem, data );
domPromise.done( data.restorePreInfuseState.bind( data, state ) );
} );
}
@@ -684,7 +737,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
infused.$element.removeData( 'ooui-infused-children' );
return infused;
}
- if ( value.html ) {
+ if ( value.html !== undefined ) {
return new OO.ui.HtmlSnippet( value.html );
}
}
@@ -1205,7 +1258,7 @@ OO.ui.Element.prototype.getData = function () {
/**
* Set element data.
*
- * @param {Mixed} Element data
+ * @param {Mixed} data Element data
* @chainable
*/
OO.ui.Element.prototype.setData = function ( data ) {
@@ -1266,6 +1319,7 @@ OO.ui.Element.prototype.getTagName = function () {
/**
* Check if the element is attached to the DOM
+ *
* @return {boolean} The element is attached to the DOM
*/
OO.ui.Element.prototype.isElementAttached = function () {
@@ -1726,6 +1780,7 @@ OO.ui.mixin.TabIndexedElement.prototype.getTabIndex = function () {
* See the [OOjs UI documentation on MediaWiki] [1] for examples.
*
* [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
+ *
* @abstract
* @class
*
@@ -2577,7 +2632,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) {
*
* The title is displayed when a user moves the mouse over the indicator.
*
- * @param {string|Function|null} indicator Indicator title text, a function that returns text, or
+ * @param {string|Function|null} indicatorTitle Indicator title text, a function that returns text, or
* `null` for no indicator title
* @chainable
*/
@@ -3051,7 +3106,7 @@ OO.ui.mixin.TitledElement = function OoUiMixinTitledElement( config ) {
this.title = null;
// Initialization
- this.setTitle( config.title || this.constructor.static.title );
+ this.setTitle( config.title !== undefined ? config.title : this.constructor.static.title );
this.setTitledElement( config.$titled || this.$element );
};
@@ -3203,7 +3258,7 @@ OO.ui.mixin.AccessKeyedElement.prototype.setAccessKeyedElement = function ( $acc
/**
* Set accesskey.
*
- * @param {string|Function|null} accesskey Key, a function that returns a key, or `null` for no accesskey
+ * @param {string|Function|null} accessKey Key, a function that returns a key, or `null` for no accesskey
* @chainable
*/
OO.ui.mixin.AccessKeyedElement.prototype.setAccessKey = function ( accessKey ) {
@@ -4049,6 +4104,9 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () {
ccWidth + ccOffset.left :
( scOffset.left + scrollLeft + scWidth ) - ccOffset.left;
desiredHeight = ( scOffset.top + scrollTop + scHeight ) - ccOffset.top;
+ // It should never be desirable to exceed the dimensions of the browser viewport... right?
+ desiredWidth = Math.min( desiredWidth, document.documentElement.clientWidth );
+ desiredHeight = Math.min( desiredHeight, document.documentElement.clientHeight );
allotedWidth = Math.ceil( desiredWidth - extraWidth );
allotedHeight = Math.ceil( desiredHeight - extraHeight );
naturalWidth = this.$clippable.prop( 'scrollWidth' );
@@ -4466,6 +4524,7 @@ OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
/**
* Set popup alignment
+ *
* @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
* `backwards` or `forwards`.
*/
@@ -4480,6 +4539,7 @@ OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
/**
* Get popup alignment
+ *
* @return {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
* `backwards` or `forwards`.
*/
@@ -5188,7 +5248,7 @@ OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
/**
* Scroll item into view, preventing spurious mouse highlight actions from happening.
*
- * @return {OO.ui.OptionWidget} Item to scroll into view
+ * @param {OO.ui.OptionWidget} item Item to scroll into view
*/
OO.ui.SelectWidget.prototype.scrollItemIntoView = function ( item ) {
var widget = this;
@@ -5977,6 +6037,7 @@ OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
/**
* Update menu item visibility after input changes.
+ *
* @protected
*/
OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () {
@@ -6052,6 +6113,7 @@ OO.ui.MenuSelectWidget.prototype.unbindKeyPressListener = function () {
*
* Note that âchooseâ should never be modified programmatically. A user can choose an option with the keyboard
* or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
+ *
* @param {OO.ui.OptionWidget} item Item to choose
* @chainable
*/
@@ -6556,22 +6618,19 @@ OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positionin
closestScrollableOfContainer = OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0 ] );
closestScrollableOfFloatable = OO.ui.Element.static.getClosestScrollableContainer( this.$floatable[ 0 ] );
- if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
- // If the scrollable is the root, we have to listen to scroll events
- // on the window because of browser inconsistencies (or do we? someone should verify this)
- if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
- closestScrollableOfContainer = OO.ui.Element.static.getWindow( closestScrollableOfContainer );
- }
+ this.needsCustomPosition = closestScrollableOfContainer !== closestScrollableOfFloatable;
+ // If the scrollable is the root, we have to listen to scroll events
+ // on the window because of browser inconsistencies.
+ if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
+ closestScrollableOfContainer = OO.ui.Element.static.getWindow( closestScrollableOfContainer );
}
if ( positioning ) {
this.$floatableWindow = $( this.getElementWindow() );
this.$floatableWindow.on( 'resize', this.onFloatableWindowResizeHandler );
- if ( closestScrollableOfContainer !== closestScrollableOfFloatable ) {
- this.$floatableClosestScrollable = $( closestScrollableOfContainer );
- this.$floatableClosestScrollable.on( 'scroll', this.onFloatableScrollHandler );
- }
+ this.$floatableClosestScrollable = $( closestScrollableOfContainer );
+ this.$floatableClosestScrollable.on( 'scroll', this.onFloatableScrollHandler );
// Initial position after visible
this.position();
@@ -6593,6 +6652,50 @@ OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positionin
return this;
};
+/**
+ * Check whether the bottom edge of the given element is within the viewport of the given container.
+ *
+ * @private
+ * @param {jQuery} $element
+ * @param {jQuery} $container
+ * @return {boolean}
+ */
+OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function ( $element, $container ) {
+ var elemRect, contRect,
+ topEdgeInBounds = false,
+ leftEdgeInBounds = false,
+ bottomEdgeInBounds = false,
+ rightEdgeInBounds = false;
+
+ elemRect = $element[ 0 ].getBoundingClientRect();
+ if ( $container[ 0 ] === window ) {
+ contRect = {
+ top: 0,
+ left: 0,
+ right: document.documentElement.clientWidth,
+ bottom: document.documentElement.clientHeight
+ };
+ } else {
+ contRect = $container[ 0 ].getBoundingClientRect();
+ }
+
+ if ( elemRect.top >= contRect.top && elemRect.top <= contRect.bottom ) {
+ topEdgeInBounds = true;
+ }
+ if ( elemRect.left >= contRect.left && elemRect.left <= contRect.right ) {
+ leftEdgeInBounds = true;
+ }
+ if ( elemRect.bottom >= contRect.top && elemRect.bottom <= contRect.bottom ) {
+ bottomEdgeInBounds = true;
+ }
+ if ( elemRect.right >= contRect.left && elemRect.right <= contRect.right ) {
+ rightEdgeInBounds = true;
+ }
+
+ // We only care that any part of the bottom edge is visible
+ return bottomEdgeInBounds && ( leftEdgeInBounds || rightEdgeInBounds );
+};
+
/**
* Position the floatable below its container.
*
@@ -6607,6 +6710,17 @@ OO.ui.mixin.FloatableElement.prototype.position = function () {
return this;
}
+ if ( !this.isElementInViewport( this.$floatableContainer, this.$floatableClosestScrollable ) ) {
+ this.$floatable.addClass( 'oo-ui-floatableElement-hidden' );
+ return;
+ } else {
+ this.$floatable.removeClass( 'oo-ui-floatableElement-hidden' );
+ }
+
+ if ( !this.needsCustomPosition ) {
+ return;
+ }
+
pos = OO.ui.Element.static.getRelativePosition( this.$floatableContainer, this.$floatable.offsetParent() );
// Position under container
@@ -6738,7 +6852,8 @@ OO.ui.InputWidget = function OoUiInputWidget( config ) {
OO.ui.InputWidget.parent.call( this, config );
// Properties
- this.$input = this.getInputElement( config );
+ // See #reusePreInfuseDOM about config.$input
+ this.$input = config.$input || this.getInputElement( config );
this.value = '';
this.inputFilter = config.inputFilter;
@@ -6822,9 +6937,8 @@ OO.ui.InputWidget.static.gatherPreInfuseState = function ( node, config ) {
* @param {Object} config Configuration options
* @return {jQuery} Input element
*/
-OO.ui.InputWidget.prototype.getInputElement = function ( config ) {
- // See #reusePreInfuseDOM about config.$input
- return config.$input || $( '' );
+OO.ui.InputWidget.prototype.getInputElement = function () {
+ return $( '' );
};
/**
@@ -7013,12 +7127,17 @@ OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) {
* @cfg {boolean} [useInputTag=false] Use an `` tag instead of a `` tag, the default.
* Widgets configured to be an `` do not support {@link #icon icons} and {@link #indicator indicators},
* non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
- * be set to `true` when thereâs need to support IE6 in a form with multiple buttons.
+ * be set to `true` when thereâs need to support IE 6 in a form with multiple buttons.
*/
OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
// Configuration initialization
config = $.extend( { type: 'button', useInputTag: false }, config );
+ // See InputWidget#reusePreInfuseDOM about config.$input
+ if ( config.$input ) {
+ config.$input.empty();
+ }
+
// Properties (must be set before parent constructor, which calls #setValue)
this.useInputTag = config.useInputTag;
@@ -7064,10 +7183,6 @@ OO.ui.ButtonInputWidget.static.supportsSimpleLabel = false;
*/
OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
var type;
- // See InputWidget#reusePreInfuseDOM about config.$input
- if ( config.$input ) {
- return config.$input.empty();
- }
type = [ 'button', 'submit', 'reset' ].indexOf( config.type ) !== -1 ? config.type : 'button';
return $( '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + type + '">' );
};
@@ -7082,22 +7197,20 @@ OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
* @chainable
*/
OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
- OO.ui.mixin.LabelElement.prototype.setLabel.call( this, label );
+ if ( typeof label === 'function' ) {
+ label = OO.ui.resolveMsg( label );
+ }
if ( this.useInputTag ) {
- if ( typeof label === 'function' ) {
- label = OO.ui.resolveMsg( label );
- }
- if ( label instanceof jQuery ) {
- label = label.text();
- }
- if ( !label ) {
+ // Discard non-plaintext labels
+ if ( typeof label !== 'string' ) {
label = '';
}
+
this.$input.val( label );
}
- return this;
+ return OO.ui.mixin.LabelElement.prototype.setLabel.call( this, label );
};
/**
@@ -7289,6 +7402,11 @@ OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) {
// Configuration initialization
config = config || {};
+ // See InputWidget#reusePreInfuseDOM about config.$input
+ if ( config.$input ) {
+ config.$input.addClass( 'oo-ui-element-hidden' );
+ }
+
// Properties (must be done before parent constructor which calls #setDisabled)
this.dropdownWidget = new OO.ui.DropdownWidget( config.dropdown );
@@ -7319,11 +7437,7 @@ OO.mixinClass( OO.ui.DropdownInputWidget, OO.ui.mixin.TitledElement );
* @inheritdoc
* @protected
*/
-OO.ui.DropdownInputWidget.prototype.getInputElement = function ( config ) {
- // See InputWidget#reusePreInfuseDOM about config.$input
- if ( config.$input ) {
- return config.$input.addClass( 'oo-ui-element-hidden' );
- }
+OO.ui.DropdownInputWidget.prototype.getInputElement = function () {
return $( '' ).attr( 'type', 'hidden' );
};
@@ -7698,7 +7812,7 @@ OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string} [type='text'] The value of the HTML `type` attribute: 'text', 'password', 'search',
- * 'email' or 'url'. Ignored if `multiline` is true.
+ * 'email', 'url' or 'date'. Ignored if `multiline` is true.
*
* Some values of `type` result in additional behaviors:
*
@@ -7798,7 +7912,7 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
.append( this.$icon, this.$indicator );
this.setReadOnly( !!config.readOnly );
this.updateSearchIndicator();
- if ( config.placeholder ) {
+ if ( config.placeholder !== undefined ) {
this.$input.attr( 'placeholder', config.placeholder );
}
if ( config.maxLength !== undefined ) {
@@ -8099,8 +8213,8 @@ OO.ui.TextInputWidget.prototype.adjustSize = function () {
.val( '' );
maxInnerHeight = this.$clone.innerHeight();
- // Difference between reported innerHeight and scrollHeight with no scrollbars present
- // Equals 1 on Blink-based browsers and 0 everywhere else
+ // Difference between reported innerHeight and scrollHeight with no scrollbars present.
+ // This is sometimes non-zero on Blink-based browsers, depending on zoom level.
measurementError = maxInnerHeight - this.$clone[ 0 ].scrollHeight;
idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
@@ -8154,7 +8268,7 @@ OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
* @private
*/
OO.ui.TextInputWidget.prototype.getSaneType = function ( config ) {
- var type = [ 'text', 'password', 'search', 'email', 'url' ].indexOf( config.type ) !== -1 ?
+ var type = [ 'text', 'password', 'search', 'email', 'url', 'date' ].indexOf( config.type ) !== -1 ?
config.type :
'text';
return config.multiline ? 'multiline' : type;
@@ -8197,7 +8311,16 @@ OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
this.focus();
- input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
+ try {
+ input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
+ } catch ( e ) {
+ // IE throws an exception if you call setSelectionRange on a unattached DOM node.
+ // Rather than expensively check if the input is attached every time, just check
+ // if it was the cause of an error being thrown. If not, rethrow the error.
+ if ( this.getElementDocument().body.contains( input ) ) {
+ throw e;
+ }
+ }
return this;
};
@@ -8617,6 +8740,7 @@ OO.inheritClass( OO.ui.ComboBoxInputWidget, OO.ui.TextInputWidget );
/**
* Get the combobox's menu.
+ *
* @return {OO.ui.FloatingMenuSelectWidget} Menu widget
*/
OO.ui.ComboBoxInputWidget.prototype.getMenu = function () {
@@ -8625,6 +8749,7 @@ OO.ui.ComboBoxInputWidget.prototype.getMenu = function () {
/**
* Get the combobox's text input widget.
+ *
* @return {OO.ui.TextInputWidget} Text input widget
*/
OO.ui.ComboBoxInputWidget.prototype.getInput = function () {
@@ -8772,6 +8897,7 @@ OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
* Please see the [OOjs UI documentation on MediaWiki] [1] for examples and more information.
*
* [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ *
* @class
* @extends OO.ui.Layout
* @mixins OO.ui.mixin.LabelElement