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=9be8e3abcc7efe93f6c117a4b03e358c733d6f53;hpb=ce3a0d7ffad905bc04dcb6a0031a404e2fcc1cc8;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 9be8e3abcc..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.1 + * 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-03-01T21:50:12Z + * Date: 2016-03-16T19:20:22Z */ ( function ( OO ) { @@ -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 )`. * @@ -685,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 ); } } @@ -3054,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 ); }; @@ -4052,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' ); @@ -6563,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(); @@ -6600,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. * @@ -6614,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 @@ -6745,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; @@ -6829,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 $( '' ); }; /** @@ -7020,12 +7127,17 @@ OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) { * @cfg {boolean} [useInputTag=false] Use an `` tag instead of a `