'scripts' => [
'resources/src/mediawiki.htmlform.checker.js',
],
+ 'dependencies' => [
+ 'mediawiki.util',
+ ],
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.htmlform.ooui' => [
// FIXME: mw.htmlform.Element also sets this to empty object
mw.htmlform = {};
- function debounce( delay, callback ) {
- var timeout;
- return function () {
- clearTimeout( timeout );
- timeout = setTimeout( Function.prototype.apply.bind( callback, this, arguments ), delay );
- };
- }
-
/**
* @class mw.htmlform.Checker
*/
if ( $extraElements ) {
$e = $e.add( $extraElements );
}
- $e.on( events, debounce( 1000, this.validate.bind( this ) ) );
+ $e.on( events, mw.util.debounce( 1000, this.validate.bind( this ) ) );
return this;
};
return escapeIdInternal( str, config.FragmentMode[ 0 ] );
},
+ /**
+ * Return a wrapper function that is debounced for the given duration.
+ *
+ * When it is first called, a timeout is scheduled. If before the timer
+ * is reached the wrapper is called again, it gets rescheduled for the
+ * same duration from now until it stops being called. The original function
+ * is called from the "tail" of such chain, with the last set of arguments.
+ *
+ * @since 1.34
+ * @param {number} delay Time in milliseconds
+ * @param {Function} callback
+ * @return {Function}
+ */
+ debounce: function ( delay, callback ) {
+ var timeout;
+ return function () {
+ clearTimeout( timeout );
+ timeout = setTimeout( Function.prototype.apply.bind( callback, this, arguments ), delay );
+ };
+ },
+
/**
* Encode page titles for use in a URL
*
assert.strictEqual( mw.util.escapeRegExp( normal ), normal, 'Alphanumerals are left alone' );
} );
+
+ QUnit.test( 'debounce', function ( assert ) {
+ var fn,
+ q = [],
+ done = assert.async();
+
+ fn = mw.util.debounce( 0, function ( data ) {
+ q.push( data );
+ } );
+
+ fn( 1 );
+ fn( 2 );
+ fn( 3 );
+
+ setTimeout( function () {
+ assert.deepEqual(
+ q,
+ [ 3 ],
+ 'Last one ran'
+ );
+ done();
+ } );
+ } );
}() );