javascriptajaxxmlhttprequestmonkeypatching

How can I modify the XMLHttpRequest responsetext received by another function?


I am trying to modify the responseText received by a function that I cannot modify. This function creates a XMLHttpRequest that I can attach to, but I have been unable to "wrap" the responseText in a way that allows me to modify the content before the original function receives it.

Here's the full original function:

function Mj(a, b, c, d, e) {
    function k() {
        4 == (m && 'readyState' in m ? m.readyState : 0) && b && ff(b) (m)
    }
    var m = new XMLHttpRequest;
    'onloadend' in m ? m.addEventListener('loadend', k, !1)  : m.onreadystatechange = k;
    c = ('GET').toUpperCase();
    d = d || '';
    m.open(c, a, !0);
    m.send(d);
    return m
}
function ff(a) {
    return a && window ? function () {
        try {
            return a.apply(this, arguments)
        } catch(b) {
            throw jf(b),
                b;
        }
    } : a
}

I have also tried to manipulate the reiceiving function k(); in an attempt to reach my goal, but since it doesn't depend on any data passing to the function (for example k(a.responseText);) I had no success.

Is there any way that I can achieve this? I do not wish to use js libraries (such as jQuery);


EDIT: I understand that I cannot change .responseText directly since it is read-only, but I am trying to find a way to change the content between the response and receiving function.


EDIT2: Added below one of the methods I have tried to intercept and change .responseText which has been addapted from here: Monkey patch XMLHTTPRequest.onreadystatechange

(function (open) {
XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
    if(/results/.test(url)) {
      console.log(this.onreadystatechange);
        this.addEventListener("readystatechange", function () {
            console.log('readystate: ' + this.readyState);
            if(this.responseText !== '') {
                this.responseText = this.responseText.split('&')[0];
            }
        }, false);
    }
    open.call(this, method, url, async, user, pass);
};
})(XMLHttpRequest.prototype.open);

EDIT3: I forgot to include that the functions Mj and ff are not globally available, they are both contained inside an anonymous function (function(){functions are here})();


EDIT4: I have changed the accepted answer because AmmarCSE's does not have any of the problems and complexity linked to jfriend00's answer.

The best answer explained in short is as follows:

Listen to whichever request you want to modify (make sure your listener will intercept it before the original function destination does, otherwise there is no point in modifying it after the response has already been used).

Save the original response (if you want to modify it) in a temporary variable

Change the property you want to modify to "writable: true", it will erase whichever value it had. In my case I use

Object.defineProperty(event, 'responseText', {
    writable: true
});

Where event is the object returned by listening to the load or readystatechange event of the xhr request

Now you can set anything you want for your response, if all you wanted was to modify the original response then you can use that data from your temporary variable and then save the modifications in the response.


Solution

  • One very simple workaround is to change the property descriptor for responseText itself

    Object.defineProperty(wrapped, 'responseText', {
         writable: true
    });
    

    So, you can extend XMLHttpRequest like

    (function(proxied) {
        XMLHttpRequest = function() {
            //cannot use apply directly since we want a 'new' version
            var wrapped = new(Function.prototype.bind.apply(proxied, arguments));
    
            Object.defineProperty(wrapped, 'responseText', {
                writable: true
            });
    
            return wrapped;
        };
    })(XMLHttpRequest);
    

    Demo