formula_equation_preview.js 6.51 KB
Newer Older
1 2 3 4 5 6
var formulaEquationPreview = {
    minDelay: 300,  // Minimum time between requests sent out.
    errorDelay: 1500  // Wait time before showing error (prevent frustration).
};

/** Setup the FormulaEquationInputs and associated javascript code. */
7
formulaEquationPreview.enable = function() {
8 9 10 11 12 13 14 15
    /**
     * Accumulate all the variables and attach event handlers.
     * This includes rate-limiting `sendRequest` and creating a closure for
     * its callback.
     */
    function setupInput() {
        var $this = $(this); // cache the jQuery object

16
        var $preview = $('#' + this.id + '_preview');
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
        var inputData = {
            // These are the mutable values

            lastSent: 0,
            isWaitingForRequest: false,
            requestVisible: 0,
            errorDelayTimeout: null,

            // The following don't change

            // Find the URL from the closest parent problems-wrapper.
            url: $this.closest('.problems-wrapper').data('url'),
            // Grab the input id from the input.
            inputId: $this.data('input-id'),

            // Store the DOM/MathJax elements in which visible output occurs.
            $preview: $preview,
Peter Baratta committed
34
            jax: null,  // Fill this in later.
35
            $img: $preview.find('img.loading'),
36 37 38 39 40 41 42 43 44 45 46 47 48 49

            requestCallback: null  // Fill it in in a bit.
        };

        // Give callback access to `inputData` (fill in first parameter).
        inputData.requestCallback = _.partial(updatePage, inputData);

        // Limit `sendRequest` and have it show the loading icon.
        var throttledRequest = _.throttle(
            sendRequest,
            formulaEquationPreview.minDelay,
            {leading: false}
        );
        // The following acts as a closure of `inputData`.
50
        var initializeRequest = function() {
51 52 53
            // Show the loading icon.
            inputData.$img.css('visibility', 'visible');

54
            // Say we are waiting for request.
55
            inputData.isWaitingForRequest = true;
56
            // First thing in `sendRequest`, say we aren't anymore.
57 58 59
            throttledRequest(inputData, this.value);
        };

60
        if (!$this.data('inputInitialized')) {
61 62 63 64
            // Hack alert: since this javascript file is loaded every time a
            // problem with mathjax preview is loaded, we wrap this step in this
            // condition to make sure we don't attach multiple event listeners
            // per math input if multiple such problems are loaded on a page.
65
            $this.on('input', initializeRequest);
66 67 68
            // Ask for initial preview.
            initializeRequest.call(this);
            // indicates that the initial preview is done for current $this!
69
            $this.data('inputInitialized', true);
70
        }
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    }

    /**
     * Fire off a request for a preview of the current value.
     * Also send along the time it was sent, and store that locally.
     */
    function sendRequest(inputData, formula) {
        // Save the time.
        var now = Date.now();
        inputData.lastSent = now;
        // We're sending it.
        inputData.isWaitingForRequest = false;

        if (formula) {
            // Send the request.
            Problem.inputAjax(
                inputData.url,
                inputData.inputId,
                'preview_formcalc',
90
                {'formula': formula, 'request_start': now},
91 92 93 94 95 96
                inputData.requestCallback
            );
            // ).fail(function () {
            //     // This is run when ajax call fails.
            //     // Have an error message and other stuff here?
            //     inputData.$img.css('visibility', 'hidden');
97
            // });
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        }
        else {
            inputData.requestCallback({
                preview: '',
                request_start: now
            });
        }
    }

    /**
     * Respond to the preview request if need be.
     * Stop if it is outdated (i.e. a later request arrived back earlier)
     * Otherwise:
     * -Refresh the MathJax
     * -Stop the loading icon if this is the most recent request
     * -Save which request is visible
     */
    function updatePage(inputData, response) {
        var requestStart = response['request_start'];
        if (requestStart == inputData.lastSent &&
            !inputData.isWaitingForRequest) {
            // Disable icon.
            inputData.$img.css('visibility', 'hidden');
        }

        if (requestStart <= inputData.requestVisible) {
            // This is an old request.
            return;
        }

        // Save the value of the last response displayed.
        inputData.requestVisible = requestStart;

        // Prevent an old error message from showing.
        if (inputData.errorWaitTimeout != null) {
            window.clearTimeout(inputData.errorWaitTimeout);
        }

        function display(latex) {
137 138
            MathJax.Hub.Startup.signal.Interest(function(message) {
                if (message === 'End') {
139
                    var previewElement = inputData.$preview[0];
140
                    MathJax.Hub.Queue(function() {
141 142 143
                        inputData.jax = MathJax.Hub.getAllJax(previewElement)[0];
                    });

144
                    MathJax.Hub.Queue(function() {
145 146 147 148
                        // Check if MathJax is loaded
                        if (inputData.jax) {
                            // Set the text as the latex code, and then update the MathJax.
                            MathJax.Hub.Queue(
149
                                ['Text', inputData.jax, latex]
150 151
                            );
                        } else if (latex) {
152
                            console.log('[FormulaEquationInput] Oops no mathjax for ', latex);
153 154
                            // Fall back to modifying the actual element.
                            var textNode = previewElement.childNodes[0];
155 156
                            textNode.data = '\\(' + latex + '\\)';
                            MathJax.Hub.Queue(['Typeset', MathJax.Hub, previewElement]);
157 158 159 160
                        }
                    });
                }
            });
161 162 163 164
        }

        if (response.error) {
            inputData.$img.css('visibility', 'visible');
165 166
            inputData.errorWaitTimeout = window.setTimeout(function() {
                display('\\text{' + response.error + '}');
167 168 169 170 171 172 173 174 175 176 177 178
                inputData.$img.css('visibility', 'hidden');
            }, formulaEquationPreview.errorDelay);
        } else {
            display(response.preview);
        }
    }

    // Invoke the setup method.
    $('.formulaequationinput input').each(setupInput);
};

formulaEquationPreview.enable();