1

I appreciate if anyone can tell me how to intercept a function call in javascript. I know it is possible with making use of proxies.

for example I tried the code below to intercept it but now I want to intercept toDataURL(). in order to call toDataURL you need to create a canvas element first.So, now I want to know how is this possible to define a proxy to intercept toDataURL().

Example code to intercept it :

window.x = 0;
    let calls = (function(){
        let canvas = document.createElement('canvas');
        let fun = canvas.toDataURL;
        canvas.toDataURL = function(){       
            window.x++;         
            return fun.apply(document, arguments);
        }
        return ()=>calls;
    })();
4
  • To me it looks like You already did modify canvas.toDataURL and not, as you say, document.createElement. I just wonder what return ()=>calls; is for, inside the IIFE. Besides that this approach also is not a generic one; it modifies the method of exactly the created element. One might try modifying HTMLCanvasElement.prototype.toDataURL instead. Commented Mar 6, 2018 at 9:39
  • Hi, I tried the code above but it did not work. could you please give an example? Commented Mar 6, 2018 at 9:53
  • Try Events, much simpler and faster than intercepting Commented Mar 6, 2018 at 10:27
  • @Bathooman ... there are now two approaches ... one based on method-modification/function-composition and another, Proxy based one. Each proving itself by working example code. Commented Mar 6, 2018 at 15:45

1 Answer 1

1

Though one always should have a good reason for modifying standard methods of standard types, the base approach of method modification in JS is wrapping. One might even think about standardizing Function.prototype[before|after|around|afterThrowing|afterFinally]. Then modifying any given method or function will be as easy as with the next provided example that also might be the answer the OP is looking for ...

(function (Function) {
  var
    isFunction = function (type) {
      return (
           (typeof type == "function")
        && (typeof type.call == "function")
        && (typeof type.apply == "function")
      );
    },
    getSanitizedTarget = function (target) {
      return ((target != null) && target) || null;
    };

  Function.prototype.before = function (handler, target) { // before
    target = getSanitizedTarget(target);
    var proceed = this ;

    return (isFunction(handler) && isFunction(proceed) && function () {
      var args = arguments;

      handler.call((target || this), args);
      return proceed.apply((target || this), args);

    }) || proceed;
  };
  Function.prototype.after = function (handler, target) { // afterReturning
    target = getSanitizedTarget(target);
    var proceed = this ;

    return (isFunction(handler) && isFunction(proceed) && function () {
      var ret, args = arguments;

      ret = proceed.apply((target || this), args);
      handler.call((target || this), ret, args);

      return ret;

    }) || proceed;
  };
  Function.prototype.around = function (handler, target) { // around
    target = getSanitizedTarget(target);

    var proceed = this ;
    return (isFunction(handler) && isFunction(proceed) && function () {

      return handler.call((target || this), proceed, handler, arguments);

    }) || proceed;
  };
}(Function));


function modifyCanvasToDataUrlAfter(returnValue, thisArgs) {
  console.log('modifyCanvasToDataUrlAfter :: thisArgs : ', thisArgs);
  console.log('modifyCanvasToDataUrlAfter :: returnValue : ', returnValue);
}
HTMLCanvasElement.prototype.toDataURL = HTMLCanvasElement.prototype.toDataURL.after(modifyCanvasToDataUrlAfter);


var elmToJpgLow = document.getElementById('canvasToLowResolutionJpg');
var elmToJpgMedium = document.getElementById('canvasToMediumResolutionJpg');

console.log("elmToJpgLow.toDataURL('image/jpeg', 0.1) : ", elmToJpgLow.toDataURL('image/jpeg', 0.1));
console.log('elmToJpgLow.toDataURL : ', elmToJpgLow.toDataURL);

console.log("elmToJpgMedium.toDataURL('image/jpeg', 0.1) : ", elmToJpgMedium.toDataURL('image/jpeg', 0.5));
console.log('elmToJpgMedium.toDataURL : ', elmToJpgMedium.toDataURL);
.as-console-wrapper { max-height: 100%!important; top: 0; }
<canvas id="canvasToLowResolutionJpg" width="5" height="5"></canvas>
<canvas id="canvasToMediumResolutionJpg" width="5" height="5"></canvas>

Edit

A Proxy based approach might look like the following provided example ...

const canvasToDataUrlModifier = {
  apply: function(target, thisArg, argumentsList) {

    let returnValue = target.apply(thisArg, argumentsList);

    console.log('toDataUrlAfterModifier :: argumentsList : ', argumentsList);
    console.log('toDataUrlAfterModifier :: returnValue : ', returnValue);

    return returnValue;
  }
};

var elmToJpgLow = document.getElementById('canvasToLowResolutionJpg');
var elmToJpgMedium = document.getElementById('canvasToMediumResolutionJpg');

var proxyToJpgLow = new Proxy(elmToJpgLow.toDataURL, canvasToDataUrlModifier);
var proxyToJpgMedium = new Proxy(elmToJpgMedium.toDataURL, canvasToDataUrlModifier);

console.log("proxyToJpgLow.call(elmToJpgLow, 'image/jpeg', 0.1) : ", proxyToJpgLow.call(elmToJpgLow, 'image/jpeg', 0.1));

console.log("proxyToJpgMedium.call(elmToJpgMedium, 'image/jpeg', 0.5) : ", proxyToJpgMedium.call(elmToJpgMedium, 'image/jpeg', 0.5));
.as-console-wrapper { max-height: 100%!important; top: 0; }
<canvas id="canvasToLowResolutionJpg" width="5" height="5"></canvas>
<canvas id="canvasToMediumResolutionJpg" width="5" height="5"></canvas>

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you for your response peter, You know what I'm actually going to do is to be noticed when canvas.toDataURL() is called. It seems I cannot get that using your proposed way. I already did this for document.createElement by using code below: let calls = (function(){ let fun = document.createElement; document.createElement = function(){ window.doc.push(arguments); return fun.apply(document, arguments); } return ()=>calls; })();
@Bathooman ... you're welcome. And please describe, what from both approaches does not work for you. All given examples do provide running code. Thus one should be able to more easily understand how each approach does operate. On the other hand, I do not get what the code provided in the Q (see my comment there) as well as the one from your comment above intend to achieve. I don't know what you expect it to do and where, why and with which error messages it fails. Being provided with a bigger picture will be more helpful.
Dear peter, I just want to be informed when canvas.toDataURL() is called. i really don't know what to do. please help!!
document.createElement can be overridden with: var origParseFloat = parseFloat; parseFloat = function(str) { alert("And I'm in your floats!"); return origParseFloat(str); }
@Bathooman ... please first respond to the comment from March 7, 11:08 ... any further attempt of answering was just guessing as long as one does not know which part of the code invokes canvas.toDataURL() and which part has to intercept or needs to be notified about it. My answer from March 6, 10:38 offers two different approaches, each approach providing working example code. What did you try? Where got you stuck?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.