I'm working on a jQuery plugin that allows you to log any javascript class or object. The idea is to override each function inside the object, or prototype of a function.
(function($)
{
"use strict";
$.log = function(object, logger)
{
if (!$.isFunction(logger))
{
logger = function(name, args)
{
console.log(name + "(" + $.makeArray(args).join(", ") + ")");
};
}
var s = $.isFunction(object) ? object.prototype : object;
for (name in s)
{
var fn = s[name];
if ($.isFunction(fn))
{
s[name] = (function(name, fn)
{
return function()
{
logger(name, arguments);
return fn.apply(this, arguments);
};
})(name, fn);
}
}
};
})(jQuery);
This seems to work for logging individual plugins. For example $.log($.ui.tabs);
logs all the function calls inside the tabs prototype.
But when I want to log all of jQuery $.log($);
it's giving me some reference error.
I can't figure out why I'm getting this error. I'm under the impression it has something to do with either this
or the arguments being passed, but I'm not sure.
Edit: Now I think about It some more it might also be caused because the overridden function always returns.
I created a fiddle to demo the problem: http://jsfiddle.net/Sj6xN/4/
EDIT:
This is the code i ended up with, so far working perfectly:
(function($)
{
"use strict";
var Logger = function(options)
{
this.options = $.extend(this.defaults, options);
};
Logger.prototype = {
defaults:
{
inherited: false,
deep: false,
logWriter: function(name, args)
{
console.log("CALL: " + name + "(" + $.makeArray(args).join(", ") + ")");
}
},
augment: function(object)
{
var self = this;
// Make sure this object is not already augmented
if (object.__isAugmented__)
{
return;
}
// Set 'isAugmented' to prevent recursion
object.__isAugmented__ = true;
// Loop through the object
for (var name in object)
{
var originalFunction = object[name];
// If it's a function and the function is not inherited or 'inherited' is enabled augment it
if ($.isFunction(originalFunction) && (object.hasOwnProperty(name) || self.options.inherited))
{
// Wrap in self executing function so references to 'name' and 'orginalFunction' are maintained
object[name] = (function(name, originalFunction)
{
// If the function has a prototype and 'deep' is enabled augment that as well
if (self.options.deep && originalFunction.prototype)
{
self.augment(originalFunction.prototype);
}
var augmentedFunction = function()
{
// Execute log writer
self.options.logWriter(name, arguments);
// Call original function
return originalFunction.apply(this, arguments);
};
// Inherit prototype of original function
augmentedFunction.prototype = originalFunction.prototype;
// Return the augmented function
return augmentedFunction;
})(name, originalFunction);
}
// If it's a plain object and 'deep' is enabled augment that as well
else if (self.options.deep && $.isPlainObject(originalFunction))
{
self.augment(originalFunction);
}
}
}
};
$.log = function(object, options)
{
var logger = new Logger(options);
// If the object is a function use it's prototype, otherwise assume a plain object
object = $.isFunction(object) ? object.prototype : object;
// Augment
logger.augment(object);
};
})(jQuery);
Can be used like this:
$.log(<object or function> [,
{
inherited: <bool>,
deep: <bool>,
logWriter: <function(name, args)>
}]);
Well look closely to the error.
Means you haven't defined name and since you are in strict mode, you can't use a variable without defining it(normally if you do it, it'll be a global variable, but not in strict mode). So if you write a
var name
before it you won't get this error anymore.Though there is another error for not having tab method. The other error says
tabs
is not a method of the object which is because when you wrap the function, you didn't inherit the prototype, so when the function is called with new, it doesn't have prototype functions(tabs
is one of them).Here's the fixed code : http://jsfiddle.net/Sj6xN/8/