mediawiki.util: Add debounce() function
authorTimo Tijhof <krinklemail@gmail.com>
Wed, 11 Sep 2019 04:46:50 +0000 (05:46 +0100)
committerKrinkle <krinklemail@gmail.com>
Wed, 11 Sep 2019 14:57:48 +0000 (14:57 +0000)
Bug: T213426
Change-Id: If370b959b2617d0f506ac3ed344af8c6a667e70d

resources/Resources.php
resources/src/mediawiki.htmlform.checker.js
resources/src/mediawiki.util/util.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index 8077022..0960e83 100644 (file)
@@ -940,6 +940,9 @@ return [
                'scripts' => [
                        'resources/src/mediawiki.htmlform.checker.js',
                ],
+               'dependencies' => [
+                       'mediawiki.util',
+               ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.ooui' => [
index 33207d4..473635a 100644 (file)
@@ -3,14 +3,6 @@
        // 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
         */
@@ -60,7 +52,7 @@
                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;
        };
index 57843cb..8521664 100644 (file)
@@ -83,6 +83,27 @@ util = {
                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
         *
index 4f61abd..a05b50b 100644 (file)
 
                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();
+               } );
+       } );
 }() );