Source: utils/ajax.js

/**
 * @namespace qui.utils.ajax
 */

import $ from '$qui/lib/jquery.module.js'

import * as ObjectUtils from '$qui/utils/object.js'
import * as Window      from '$qui/window.js'

import {asap} from './misc.js'
import URL    from './url.js'


const DEFAULT_REQUEST_TIMEOUT = 60 /* Seconds */

let pendingRequests = []


function prepareResponseHeaders(response) {
    let headersStr = response.getAllResponseHeaders()
    let headers = headersStr.split('\r\n').filter(h => h.length > 0)

    return ObjectUtils.fromEntries(headers.map(function (h) {
        let parts = h.split(':')
        let name = parts[0].trim()
        let value = parts.slice(1).join(':').trim()

        return [name, value]
    }))
}


/**
 * Return a list of currently pending requests
 * @alias qui.utils.ajax.getPendingRequests
 * @returns {jqXHR[]}
 */
export function getPendingRequests() {
    return pendingRequests.slice()
}

/**
 * Perform an AJAX JSON HTTP request and decode response as JSON.
 * @alias qui.utils.ajax.requestJSON
 * @param {String} method the HTTP method
 * @param {String} path the path or a URL to request
 * @param {Object} [query]
 * @param {*} [data] data to transmit in request body
 * @param {Function} [success] successful callback; will be called with decoded response and headers as parameters
 * @param {Function} [failure] failure callback; will be called with decoded response, status code, a result message and
 * headers as parameters
 * @param {Object} [headers] optional request headers
 * @param {Number} [timeout] optional request timeout, in seconds
 * @returns {jqXHR}
 */
export function requestJSON(
    method, path, query = null, data = null, success = null, failure = null, headers = null,
    timeout = DEFAULT_REQUEST_TIMEOUT
) {
    let contentType = null

    if (data != null) {
        data = JSON.stringify(data)
        contentType = 'application/json'
    }

    let url = URL.parse(URL.qualify(path)).alter({query: query || {}}).toString()

    let request = $.ajax({
        url: url,
        type: method,
        data: data,
        processData: false,
        cache: false,
        contentType: contentType,
        timeout: timeout * 1000,
        beforeSend: function (xhr) {
            if (headers) {
                ObjectUtils.forEach(headers, function (k, v) {
                    xhr.setRequestHeader(k, v)
                })
            }
        },
        success: function (data, status, response) {
            let headers = prepareResponseHeaders(response)

            /* Delay calling the success handler a bit so that complete handler below gets called */
            asap(function () {
                if (success) {
                    success(data, headers)
                }
            })
        },
        error: function (request, msg) {
            let headers = prepareResponseHeaders(request)

            /* Defer error handling a bit, it seems to fix call order when window closes */
            asap(function () {
                if (failure) {
                    failure(request.responseJSON || {}, request.status, msg, headers)
                }
            })
        },
        complete: function (req, status) {
            /* Remove the request from pending list */
            let index = pendingRequests.indexOf(request)
            if (index >= 0) {
                pendingRequests.splice(index, 1)
            }
        }
    })

    request.details = {
        method: method,
        path: path,
        query: query,
        data: data,
        sent: new Date()
    }

    if (!Window.isClosing()) {
        pendingRequests.push(request)
    }

    return request
}