Intercepting function calls in javascript - javascript

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;
})();

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>

Related

Madcap Flare 11 - HTML5 output throws requirejs error - Mismatched anonymous define() module: function(C

I am building the customer manual on frontend using Madcam Flare 11 HTML5 output and referring the below js file but getting the error Uncaught Error: Mismatched anonymous define() module: function(C)
<script src="scripts/helpDoc/WHP/Default.js"></script>
After i load the Madcap library and runtime i am getting the below error. This error comes at very first time when i invoke the madcap flare methods.
require.min.js:13 Uncaught Error: Mismatched anonymous define() module: function () { 'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
}
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var es6Promise = createCommonjsModule(function (module, exports) {
/*!
* #overview es6-promise - a tiny implementation of Promises/A+.
* #copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
* #license Licensed under MIT license
* See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
* #version v4.2.5+7f2b526d
*/
(function (global, factory) {
module.exports = factory();
}(commonjsGlobal, (function () { function objectOrFunction(x) {
var type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}
function isFunction(x) {
return typeof x === 'function';
}
var _isArray = void 0;
if (Array.isArray) {
_isArray = Array.isArray;
} else {
_isArray = function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
}
var isArray = _isArray;
var len = 0;
var vertxNext = void 0;
var customSchedulerFn = void 0;
var asap = function asap(callback, arg) {
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush();
}
}
};
function setScheduler(scheduleFn) {
customSchedulerFn = scheduleFn;
}
function setAsap(asapFn) {
asap = asapFn;
}
var browserWindow = typeof window !== 'undefined' ? window : undefined;
var browserGlobal = browserWindow || {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
// test for web worker but not in IE10
var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
// node
function useNextTick() {
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// see https://github.com/cujojs/when/issues/410 for details
return function () {
return process.nextTick(flush);
};
}
// vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function () {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
var iterations = 0;
var observer = new BrowserMutationObserver(flush);
var node = document.createTextNode('');
observer.observe(node, { characterData: true });
return function () {
node.data = iterations = ++iterations % 2;
};
}
// web worker
function useMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = flush;
return function () {
return channel.port2.postMessage(0);
};
}
function useSetTimeout() {
// Store setTimeout reference so es6-promise will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var globalSetTimeout = setTimeout;
return function () {
return globalSetTimeout(flush, 1);
};
}
var queue = new Array(1000);
function flush() {
for (var i = 0; i < len; i += 2) {
var callback = queue[i];
var arg = queue[i + 1];
callback(arg);
queue[i] = undefined;
queue[i + 1] = undefined;
}
len = 0;
}
function attemptVertx() {
try {
var vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch (e) {
return useSetTimeout();
}
}
var scheduleFlush = void 0;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof commonjsRequire === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}
function then(onFulfillment, onRejection) {
var parent = trequire.min.js:13)
at L (require.min.js:41)
at Object.g [as require] (require.min.js:104)
at requirejs (require.min.js:126)
at MadCap.WebHelp.HelpSystem.LoadLanguage (Default.js:269)
at Default.js:269
at MadCap.WebHelp.HelpSystem.LoadBreakpoints (Default.js:269)
at MadCap.WebHelp.HelpSystem.<anonymous> (Default.js:269)
at XMLHttpRequest.OnreadystatechangeRemote (Default.js:241)

Javascript callback using this keyword in context of function

We're trying to create our own Javascript library to replace jQuery and reduce overhead. We're wanting to call functions from the global scope using the this keyword, but the script is breaking in our foreach loop. How do we callback an object inside or custom "each" function using our $(this).getAtt('data-src') function rather than a.getAttribute('data-src')
this is defaulting to the window's object. Here's a minimized version of our library
var $=(function(){
'use strict';
var c = function(w){
if(!w)return;
if(w==='document'){this.elems=[document];}
else if(w==='window'){this.elems=[window];}
else {this.elems=document.querySelectorAll(w);}
};
c.prototype.each = function(callback){
if(!callback || typeof callback !== 'function')return;
for(var i = 0, length = this.elems.length; i < length; i++){callback(this.elems[i], i);}return this;
};
c.prototype.setAtt=function(n,v){this.each(function(item){item.setAttribute(n,v);});return this;};
c.prototype.getAtt=function(n){return this.elems[0].getAttribute(n);};
var init=function(w){return new c(w);};return init;
})();
function loadImgs(){
$("img[data-src]").each(function(a,b){
console.log(a.getAttribute('data-src'));
console.log($(this).getAtt('data-src'));
});
}
And our minimized HTML :
<a onclick="loadImgs();">lazyload</a><br>
<img src="spacer.gif" alt=""/></div><img class="lazyLoad" data-src="replacer.jpg" alt="">
Pass the calling context you want (the element) to the getAttribute method by using .call().
You also need the c constructor to set the elems property to the argument if the argument is an element:
} else if (w instanceof HTMLElement) {
this.elems = [w];
}
for (var i = 0, length = this.elems.length; i < length; i++) {
callback.call(this.elems[i], this.elems[i], i);
// ^^ "this" in callback
// ^^ first argument to callback
// ^^ second argument to callback
}
var $ = (function() {
'use strict';
var c = function(w) {
if (!w) return;
if (w === 'document') {
this.elems = [document];
} else if (w === 'window') {
this.elems = [window];
} else if (w instanceof HTMLElement) {
this.elems = [w];
} else {
this.elems = document.querySelectorAll(w);
}
};
c.prototype.each = function(callback) {
if (!callback || typeof callback !== 'function') return;
for (var i = 0, length = this.elems.length; i < length; i++) {
callback.call(this.elems[i], this.elems[i], i);
}
return this;
};
c.prototype.setAtt = function(n, v) {
this.each(function(item) {
item.setAttribute(n, v);
});
return this;
};
c.prototype.getAtt = function(n) {
return this.elems[0].getAttribute(n);
};
var init = function(w) {
return new c(w);
};
return init;
})();
function loadImgs() {
$("img[data-src]").each(function(a, b) {
console.log(a.getAttribute('data-src'));
console.log($(this).getAtt('data-src'));
});
}
<a onclick="loadImgs();">lazyload</a><br>
<img src="spacer.gif" alt="" /></div><img class="lazyLoad" data-src="replacer.jpg" alt="">

(JS) Custom, inheritable, namespace to call controllers in MVC

I am currently trying to wrap my head around creating a JavaScript namespace for organizing our JS files, that can be used to access specific methods that allow calling methods from MVC controllers. My confusion revolves around namespacing and inheriting the or applying the namespace when it is called in a different source.
Namespace TEST.js
var TEST = TEST || {}; // Needed?
(function() {
if (typeof String.prototype.trimLeft !== "function") {
String.prototype.trimLeft = function () {
return this.replace(/^\s+/, "");
};
}
if (typeof String.prototype.trimRight !== "function") {
String.prototype.trimRight = function () {
return this.replace(/\s+$/, "");
};
}
if (typeof Array.prototype.map !== "function") {
Array.prototype.map = function (callback, thisArg) {
for (var i = 0, n = this.length, a = []; i < n; i++) {
if (i in this) a[i] = callback.call(thisArg, this[i]);
}
return a;
};
}
this.Settings = {
Timer: 300
};
this.Service = (name, isApi) => {
let location = isApi === true ? "~/Controller/api/" + name : "~/Controller/" + name;
this.Call = (data, method, type, callback) => {
let method = location + "/" + method + ".cs";
let data = typeof data === "object" ? data : {};
let type = type || "GET";
callback = fetch(method, {
method: type,
headers: {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then((res) => res.json())
.then((data) => console.log(data));
return callback;
};
};
this.Functions = {
PageWidth: function () {
// return parseInt main container css width
},
setCookie: function (name, value) {
var val = value + ";domain=" + window.location.host + ";path=/;";
document.cookie = name + "=" + val;
},
deleteCookie: (name) => { document.cookie = name + "=null;domain=" + window.location.host + ";path=/;expires=Thu, 01 Jan 1900 00:00:00 GMT;"; },
getCookie: (name) => { getCookies()[name]; },
getCookies: () => {
var c = document.cookie, v = 0, cookies = {};
if (document.cookie.match(/^\s*\$Version=(?:"1"|1);\s*(.*)/)) {
c = RegExp.$1;
v = 1;
}
if (v === 0) {
c.split(/[,;]/).map(function (cookie) {
var parts = cookie.split(/=/, 2),
name = decodeURIComponent(parts[0].trimLeft()),
value = parts.length > 1 ? decodeURIComponent(parts[1].trimRight()) : null;
cookies[name] = value;
});
} else {
c.match(/(?:^|\s+)([!#$%&'*+\-.0-9A-Z^`a-z|~]+)=([!#$%&'*+\-.0-9A-Z^`a-z|~]*|"(?:[\x20-\x7E\x80\xFF]|\\[\x00-\x7F])*")(?=\s*[,;]|$)/g).map(function ($0, $1) {
var name = $0,
value = $1.charAt(0) === '"'
? $1.substr(1, -1).replace(/\\(.)/g, "$1")
: $1;
cookies[name] = value;
});
}
return cookies;
},
getQSParams: (query) => {
query
? (/^[?#]/.test(query) ? query.slice(1) : query)
.split('&')
.reduce((params, param) => {
let [key, value] = param.split('=');
params[key] = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : '';
return params;
}, {}
)
: {}
}
}
this.ClientState = {};
this.getWidth = function () {
return Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentelement.offsetWidth,
document.documentElement.clientWidth
);
}
this.getHeight = function () {
return Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.documentElement.clientHeight
);
}
}).apply(ELITE); // Var up top go here instead?
And my testing code to see if this works:
<script type="text/javascript" src='<%=ResolveUrl("~/Content/Scripts/TEST.js") %>'></script>
<script type="text/javascript">
$(function () {
var test = new TEST.Service("TestController", true);
var data = {};
test.Call(data, "TestMethod", "GET", function (resp) {
console.log(resp);
});
}(TEST));
</script>
I am sure it is something simple or a mix-up of things.
Question: Correctly namespacing a JS file to be inherited by other JS files or JS script calls for calling MVC controller methods?
Bonus: Would this be better executed as a class instead? I do have access to using ES5/ES6 and of course jQuery will be minified in with this TEST.js file!

Rxjs observing object updates and changes

I am currently trying to observe any changes to a given object including all of it's elements.
The following code only fires when an object[x] is updates, but not if individually updating object[x]'s elements such as object[x][y]
<script>
var elem = document.getElementById("test1");
var log = function(x) {
elem.innerHTML += x + "<br/><br/><br/>";
};
var a = [{a:1,b:2},
{a:2,b:5}
];
var source = Rx.Observable
.ofObjectChanges(a)
.map(function(x) {
return JSON.stringify(x);
});
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1];
</script>
This code runs and fires correctly.
however. if I instead to this
a[0]['a'] = 3;
Then nothing happens.
EDIT
A better way to phrase this, how can I observe changes from an array of objects?
If you want only the nested object changes:
var source = rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
});
If you also want changes like a[0] = a[1]:
var source = rx.Observable.merge(
rx.Observable.ofArrayChanges(a),
rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
})
);
The flatMap or selectMany (they are the same function) will allow you to iterate over a value and execute a function that returns an Observable. The values from all these Observables are "flattened" onto a new stream that is returned.
http://reactivex.io/documentation/operators/flatmap.html
Perhaps something like this by merging two Observables (one for the array and the other observing the elements of the array):
var a = [
{a:1,b:2},
{a:2,b:5}
];
var source1 = Rx.Observable.ofArrayChanges(a).map(function(x) {
return JSON.stringify(x);
});
var source2 = Rx.Observable
.fromArray(a.map(function(o, i) { return [o, i]; }))
.flatMap(function(oi) {
return Rx.Observable.ofObjectChanges(oi[0])
.map(function(x) {
var y = {
type: x.type,
object: x.object,
name: x.name,
oldValue: x.oldValue,
arrayIndex: oi[1] // pass the index of the member that changed
};
return JSON.stringify(y);
});
})
source = source1.merge(source2)
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1]
a[1]['b'] = 7
Thanks to #electrichead here we're not using concatMap because the sources that we made by ofObjectChanges and ofArrayChanges never complete.
Here's a working example of Rx.Observable.ofNestedObjectChanges simple implementation, you can get the gist of it and implement you own.
http://jsbin.com/wekote/edit?js,console
Rx.Observable.ofNestedObjectChanges = function(obj) {
if (obj == null) { throw new TypeError('object must not be null or undefined.'); }
if (typeof Object.observe !== 'function' && typeof Object.unobserve !== 'function') { throw new TypeError('Object.observe is not supported on your platform') }
return new Rx.AnonymousObservable(function(observer) {
function observerFn(changes) {
for(var i = 0, len = changes.length; i < len; i++) {
observer.onNext(changes[i]);
}
}
Object.observe(obj, observerFn);
//Recursive observers hooks - same observerFn
traverseObjectTree(obj, observerFn);
function traverseObjectTree(element, observerFn){
for(var i=0;i<Object.keys(element).length;i++){
var myObj = element[Object.keys(element)[i]];
if(typeof myObj === "object"){
Object.observe(myObj, observerFn);
traverseObjectTree(myObj,observerFn);
}
}
}
return function () {
Object.unobserve(obj, observerFn);
};
});
};
//Test
var json = {
element : {
name : "Yocto",
job : {
title: "Designer"
}
},
element1: {
name : "Mokto"
}
};
setTimeout(function(){
json.element.job.title = "A Great Designer";
},3000);
var source = Rx.Observable.ofNestedObjectChanges(json);
var subscription = source.subscribe(
function (x) {
console.log(x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
json.element.name = "Candy Joe";

What is the best practice to make an object in JS like this: T('isArray')([]) == T.run('isArray')([]) == T().run('isArray')?

What is the best practice to make an object in JavaScript like this, knowing T is the main object:
T('isArray')([])
T.run('isArray')([])
T().run('isArray')([])
T('isArray', [])
T.run('isArray', [])
T().run('isArray', [])
They all must use the same function.
Since the main object can be called it must be a function. The function should decide what to return based on the arguments:
var T = (function() {
var functions = { // define functions that can be run like isArray
isArray: function(a) {
return Array.isArray(a);
},
log: function(a, b) {
console.log(a + b);
}
};
var noop = function() {}; // function doing nothing (no operation)
var T = function(f) {
if(arguments.length >= 2) { // function + args provided
return (functions[f] || noop) // call it
.apply(this, [].slice.call(arguments, 1));
} else if(arguments.length === 1) { // only function provided
return function() { // return function that can be called with args
return (functions[f] || noop)
.apply(this, arguments);
}
} else { // nothing provided, return T itself (so that e.g. T.run === T().run)
return T;
}
}
T.run = function() { // run function
return T.apply(this, arguments);
};
T.getState = function() { // another function
console.log("Not implemented");
};
return T; // actually return T so that it gets stored in 'var T'
})();
// tests
console.log(
T('isArray')([]),
T.run('isArray')([]),
T().run('isArray')([]),
T('isArray', []),
T.run('isArray', []),
T().run('isArray', [])
);
T('log')(1, 2);
T.getState();

Categories

Resources