Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Utility functions.
18
 *
19
 * @module core/utils
20
 * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import Pending from 'core/pending';
25
 
26
 /**
27
  * Create a wrapper function to throttle the execution of the given
28
  *
29
  * function to at most once every specified period.
30
  *
31
  * If the function is attempted to be executed while it's in cooldown
32
  * (during the wait period) then it'll immediately execute again as
33
  * soon as the cooldown is over.
34
  *
35
  * @method
36
  * @param {Function} func The function to throttle
37
  * @param {Number} wait The number of milliseconds to wait between executions
38
  * @return {Function}
39
  */
40
export const throttle = (func, wait) => {
41
    let onCooldown = false;
42
    let runAgain = null;
43
    const run = function(...args) {
44
        if (runAgain === null) {
45
            // This is the first time the function has been called.
46
            runAgain = false;
47
        } else {
48
            // This function has been called a second time during the wait period
49
            // so re-run it once the wait period is over.
50
            runAgain = true;
51
        }
52
 
53
        if (onCooldown) {
54
            // Function has already run for this wait period.
55
            return;
56
        }
57
 
58
        func.apply(this, args);
59
        onCooldown = true;
60
 
61
        setTimeout(() => {
62
            const recurse = runAgain;
63
            onCooldown = false;
64
            runAgain = null;
65
 
66
            if (recurse) {
67
                run(args);
68
            }
69
        }, wait);
70
    };
71
 
72
    return run;
73
};
74
 
75
/**
76
 * @property {Map} debounceMap A map of functions to their debounced pending promises.
77
 */
78
const debounceMap = new Map();
79
 
80
/**
81
 * Create a wrapper function to debounce the execution of the given
82
 * function. Each attempt to execute the function will reset the cooldown
83
 * period.
84
 *
85
 * @method
86
 * @param {Function} func The function to debounce
87
 * @param {Number} wait The number of milliseconds to wait after the final attempt to execute
88
 * @param {Object} [options]
89
 * @param {boolean} [options.pending=false] Whether to wrap the debounced method in a pending promise
90
 * @param {boolean} [options.cancel=false] Whether to add a cancel method to the debounced function
91
 * @return {Function}
92
 */
93
export const debounce = (
94
    func,
95
    wait,
96
    {
97
        pending = false,
98
        cancel = false,
99
    } = {},
100
) => {
101
    let timeout = null;
102
 
103
    const returnedFunction = (...args) => {
104
        if (pending && !debounceMap.has(returnedFunction)) {
105
            debounceMap.set(returnedFunction, new Pending('core/utils:debounce'));
106
        }
107
        clearTimeout(timeout);
108
        timeout = setTimeout(async () => {
109
            // Get the current pending promise and immediately empty it.
110
            // This is important to allow the function to be debounced again as soon as possible.
111
            // We do not resolve it until later - but that's fine because the promise is appropriately scoped.
112
            const pendingPromise = debounceMap.get(returnedFunction);
113
            debounceMap.delete(returnedFunction);
114
 
115
            // Allow the debounced function to return a Promise.
116
            // This ensures that Behat will not continue until the function has finished executing.
117
            await func.apply(this, args);
118
 
119
            // Resolve the pending promise if it exists.
120
            pendingPromise?.resolve();
121
        }, wait);
122
    };
123
 
124
    if (cancel) {
125
        returnedFunction.cancel = () => {
126
            const pendingPromise = debounceMap.get(returnedFunction);
127
            pendingPromise?.resolve();
128
            clearTimeout(timeout);
129
        };
130
    }
131
 
132
    return returnedFunction;
133
};
134
 
135
/**
136
 * Normalise the provided component such that '', 'moodle', and 'core' are treated consistently.
137
 *
138
 * @param   {String} component
139
 * @returns {String}
140
 */
141
export const getNormalisedComponent = (component) => {
142
    if (component) {
143
        if (component !== 'moodle' && component !== 'core') {
144
            return component;
145
        }
146
    }
147
 
148
    return 'core';
149
};