File size: 3,355 Bytes
30c32c8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
 * @fileoverview
 * Many ScratchX extensions require jQuery to do things like loading scripts and making requests.
 * The real jQuery is pretty large and we'd rather not bring in everything, so this file reimplements
 * small stubs of a few jQuery methods.
 * It's just supposed to be enough to make existing ScratchX extensions work, nothing more.
 */

const log = require('../util/log');

const jQuery = () => {
    throw new Error('Not implemented');
};

jQuery.getScript = (src, callback) => {
    const script = document.createElement('script');
    script.src = src;
    if (callback) {
        // We don't implement callback arguments.
        script.onload = () => callback();
    }
    document.body.appendChild(script);
};

/**
 * @param {Record<string, any>|undefined} obj
 * @returns {URLSearchParams}
 */
const objectToQueryString = obj => {
    const params = new URLSearchParams();
    if (obj) {
        for (const key of Object.keys(obj)) {
            params.set(key, obj[key]);
        }
    }
    return params;
};

let jsonpCallback = 0;

jQuery.ajax = async (arg1, arg2) => {
    let options = {};

    if (arg1 && arg2) {
        options = arg2;
        options.url = arg1;
    } else if (arg1) {
        options = arg1;
    }

    const urlParameters = objectToQueryString(options.data);
    const getFinalURL = () => {
        const query = urlParameters.toString();
        let url = options.url;
        if (query) {
            url += `?${query}`;
        }
        // Forcibly upgrade all HTTP requests to HTTPS so that they don't error on HTTPS sites
        // All the extensions we care about work fine with this
        if (url.startsWith('http://')) {
            url = url.replace('http://', 'https://');
        }
        return url;
    };

    const successCallback = result => {
        if (options.success) {
            options.success(result);
        }
    };
    const errorCallback = error => {
        log.error(error);
        if (options.error) {
            // The error object we provide here might not match what jQuery provides but it's enough to
            // prevent extensions from throwing errors trying to access properties.
            options.error(error);
        }
    };

    try {
        if (options.dataType === 'jsonp') {
            const callbackName = `_jsonp_callback${jsonpCallback++}`;
            global[callbackName] = data => {
                delete global[callbackName];
                successCallback(data);
            };

            const callbackParameterName = options.jsonp || 'callback';
            urlParameters.set(callbackParameterName, callbackName);

            jQuery.getScript(getFinalURL());
            return;
        }

        if (options.dataType === 'script') {
            jQuery.getScript(getFinalURL(), successCallback);
            return;
        }

        const res = await fetch(getFinalURL(), {
            headers: options.headers
        });
        // dataType defaults to "Intelligent Guess (xml, json, script, or html)"
        // It happens that all the ScratchX extensions we care about either set dataType to "json" or
        // leave it blank and implicitly request JSON, so this works good enough for now.
        successCallback(await res.json());
    } catch (e) {
        errorCallback(e);
    }
};

module.exports = jQuery;