Yes, I know that. Function bind is not supported by Phantomjs. But maybe I can use something else, or say page.open not to use bind? It seems to be OK, but some websites
return error
TypeError: 'undefined' is not a function (evaluating 'b.bind(a)')
After that I wrote a simple script, which just opens a page:
var address = phantom.args[0];
if(!address) phantom.exit(1);
page = require("webpage").create();
page.open(address, function(status){
setInterval(
function () {
console.log(
page.evaluate(function() {
return document.body.innerHTML.length;
})
)}, 200)
})
But error is still there. Error is not the main problem, but the problem is to get page content, because after error page content is not loading...
So, I need help.
P.S. Problem website is http://vkrushelnytskiy.wix.com/aaaa
There is an npm package which loads a polyfill for phantomJS's missing .bind method. Copying the installation instructions here for convenience, but follow the link for any updates.
Installation
npm install --save-dev phantomjs-polyfill
Usage
require('phantomjs-polyfill')
Usage with Karma
Include the polyfill directly in the files list of your karma.conf
...
files: [
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
...
]
...
You can use this code as an alternative
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
You can shim Function.bind using the following polyfill.
Just prepend it to the code you are trying to run. There are probably nicer solutions, but this worked great for me.
You can mention this bind function just before the test case.
// PhantomJS doesn't support bind yet
Function.prototype.bind = Function.prototype.bind ||
function (ctx) {
var fn = this,
args = [],
param_length = 0;
for(var i=0; i<arguments.length; i++) {
if(i){
args[i-1] = arguments[i];
}
}
param_length = args.length;
return function () {
for(var i =0; i<arguments.length; i++){
args[param_length + i] = arguments[i];
}
return fn.apply(ctx, args);
};
};
Related
Is it possible to extend the console object?
I tried something like:
Console.prototype.log = function(msg){
Console.prototype.log.call(msg);
alert(msg);
}
But this didn't work.
I want to add additional logging to the console object via a framework like log4javascript and still use the standard console object (in cases where log4javascript is not available) in my code.
Thanks in advance!
Try following:
(function() {
var exLog = console.log;
console.log = function(msg) {
exLog.apply(this, arguments);
alert(msg);
}
})()
You Can Also add log Time in This Way :
added Momentjs or use New Date() instead of moment.
var oldConsole = console.log;
console.log = function(){
var timestamp = "[" + moment().format("YYYY-MM-DD HH:mm:ss:SSS") + "] ";
Array.prototype.unshift.call(arguments, timestamp);
oldConsole.apply(this, arguments);
};
It's really the same solution some others have given, but I believe this is the most elegant and least hacky way to accomplish this. The spread syntax (...args) makes sure not a single argument is lost.
var _console={...console}
console.log = function(...args) {
var msg = {...args}[0];
//YOUR_CODE
_console.log(...args);
}
For ECMAScript 2015 and later
You can use the newer Proxy feature from the ECMAScript 2015 standard to "hijack" the global console.log.
Source-Code
'use strict';
class Mocker {
static mockConsoleLog() {
Mocker.oldGlobalConsole = window.console;
window.console = new Proxy(window.console, {
get(target, property) {
if (property === 'log') {
return function(...parameters) {
Mocker.consoleLogReturnValue = parameters.join(' ');
}
}
return target[property];
}
});
}
static unmockConsoleLog() {
window.console = Mocker.oldGlobalConsole;
}
}
Mocker.mockConsoleLog();
console.log('hello'); // nothing happens here
Mocker.unmockConsoleLog();
if (Mocker.consoleLogReturnValue === 'hello') {
console.log('Hello world!'); // Hello world!
alert(Mocker.consoleLogReturnValue);
// anything you want to do with the console log return value here...
}
Online Demo
Repl.it.
Node.js users...
... I do not forget you. You can take this source-code and replace window.console by gloabl.console to properly reference the console object (and of course, get rid of the alert call). In fact, I wrote this code initially and tested it on Node.js.
// console aliases and verbose logger - console doesnt prototype
var c = console;
c.l = c.log,
c.e = c.error,
c.v = c.verbose = function() {
if (!myclass || !myclass.verbose) // verbose switch
return;
var args = Array.prototype.slice.call(arguments); // toArray
args.unshift('Verbose:');
c.l.apply(this, args); // log
};
// you can then do
var myclass = new myClass();
myclass.prototype.verbose = false;
// generally these calls would be inside your class
c.v('1 This will NOT log as verbose == false');
c.l('2 This will log');
myclass.verbose = true;
c.v('3 This will log');
I noted that the above use of Array.prototype.unshift.call by nitesh is a better way to add the 'Verbose:' tag.
You can override the default behavior of the console.log function using the below approach, the below example demonstrates to log the line number using the overridden function.
let line = 0;
const log = console.log;
console.log = (...data) => log(`${++line} ===>`, ...data)
console.log(11, 1, 2)
console.log(11, 1, 'some')
I'd like be able to call a function like item_edit.say hello passed as a string on the window object (like the last line of the following):
var arc={ view: { item_edit: {} } };
arc.view.item_edit={
say_hello: function(){
alert('hello there');
}
}
var f_name='say_hello';
var g_name='item_edit.say_hello';
var str=window.arc.view.item_edit[f_name](); // <- this works
var str2=window.arc.view[g_name](); // <- this is what I'm interested in; curently doesn't work
any ideas on how to get this to work?
thx in advance
edit #1
I guess I should add that probably don't want to be doing eval although the more I look at it, that might be what makes sense (and is in fact what eval was made to do).
Sure. The Google closure library does something like this in its goog.provide function when not optimized by the compiler.
function callDotted(obj, path, args) {
var parts = path ? path.split('.') : [];
var i, n = parts.length;
for (i = 0; i < n - 1; ++i) {
obj = obj[parts[i]];
}
var fn = i < n ? obj[parts[i]] : obj;
return fn.apply(obj, args);
}
and then on browsers where Date.now returns the current timestamp,
callDotted(window, 'Date.now', [])
returns the current timestamp.
Here's one way using .reduce().
var str2 = g_name.split('.').reduce(function(obj, key) {
return obj[key];
}, window.arc.view);
You'll need to shim it for older browsers, and introduce safety checks if you want.
If you do this a lot, I'd add the function to your library so you can reuse it.
function keyToObj(obj, key) {
return obj[key];
}
Then use it like this:
var str2 = g_name.split('.').reduce(keyToObj, window.arc.view);
As #MikeSamuel pointed out, there's an issue with the this value of the executed function when using this approach.
To resolve this, we could make another version that's suited specifically for method invocations.
function keyToMethod(obj, key, i, arr) {
return i === arr.length - 1 && typeof obj[key] === "function"
? function() {
return obj[key].apply(obj, arguments);
}
: obj[key];
}
Now our function returns a function that invokes the method from the proper object.
var str2 = g_name.split('.').reduce(keyToMethod, window.arc.view)();
We could further enhance the returned function to check to see if the this value is the default value, and use the provided value if not.
How about this:
var str2 = eval('window.arc.view.' + g_name + '()');
I am newbie about javascript.So I do not know what is the name of I looking for and How do I do it?
After you read question if you thing question title is wrong, you should change title.
I am using console.log for debugging but this is causing error if browser IE. I made below function for this problem.
var mylog=function(){
if (devmode && window.console){
console.log(arguments);
}
};
mylog("debugging");
Now I want to use all console functions without error and I can do that as below.
var myconsole={
log:function(){
if (devmode && window.console){
console.log(arguments);
}
}
,error:function(){
if (devmode && window.console){
console.error(arguments);
}
}
...
...
...
};
But I do not want to add all console functions to myconsole object severally.
I can do that in PHP with below code.
class MyConsole
{
function __call($func,$args)
{
if ($devmode && function_exists('Console')){
Console::$func($args); // Suppose that there is Console class.
}
}
}
MyConsole::warn("name",$name);
MyConsole::error("lastname",$lastname);
This is possible with __noSuchMethod__ method but this is specific to only firefox.
Thanks for helping.
Unfortunately, you can't do that in JavaScript, the language doesn't have support for the "no such method" concept.
Two options for you:
Option 1:
Use strings for your method name, e.g.:
function myconsole(method) {
var args;
if (devmode && window.console) {
args = Array.prototype.slice.apply(arguments, 1);
window.console[method].apply(window.console, args);
}
}
Usage:
myconsole("log", "message");
myconsole("error", "errormessage");
The meat of myconsole is here:
args = Array.prototype.slice.apply(arguments, 1);
window.console[method].apply(window.console, args);
The first line copies all of the arguments supplied to myconsole except the first one (which is the name of the method we want to use). The second line retrieves the function object for the property named by the string in method from the console object and then calls it via the JavaScript apply function, giving it those arguments.
Option 2:
A second alternative came to me which is best expressed directly in code:
var myconsole = (function() {
var methods = "log debug info warn error assert clear dir dirxml trace group groupCollapsed groupEnd time timeEnd profile profileEnd count exception table".split(' '),
index,
myconsole = {},
realconsole = window.console;
for (index = 0; index < methods.length; ++index) {
proxy(methods[index]);
}
function proxy(method) {
if (!devmode || !realconsole || typeof realconsole[method] !== 'function') {
myconsole[method] = noop;
}
else {
myconsole[method] = function() {
return realconsole[method].apply(realconsole, arguments);
};
}
}
function noop() {
}
return myconsole;
})();
Then you just call log, warn, etc., on myconsole as normal.
Is it possible to listen to any function invocation or state change
I have a object that wrap another
function wrapper(origiObj){
this.origObj = origObj;
}
var obj = wrapper(document);//this is an example
var obj = wrapper(db);//this is an example
now everytime someone tries to invoke obj.innerHTML or obj.query(..)
I would like to listen to that..
Yes, it's possible:
functions are easy, and properties has to be watched
function FlyingObject(obj){
this.obj = obj;
for(var p in obj){
if(typeof obj[p] == 'function'){
console.log(p);
this[p] = function(){
console.log("orig func");
};
}else{
this.watch(p,function(){
console.log("orig property");
});
}
}
}
var obj = {
f:function(a,b){ return a+b},
m:1
};
var fo = new FlyingObject(obj);
fo.m = 5;
fo.f(1,4);
If your browser/node.js doesn't support Object.watch, check this out:
Object.watch() for all browsers?
Yes you can, define a getter/setter for properties and a shadow function for the function like this: http://jsfiddle.net/fHRyU/1/.
function wrapper(origObj){
var type = origObj.innerHTML ? 'doc' : 'db';
if(type === "doc") {
var orig = origObj.innerHTML;
origObj.__defineGetter__('innerHTML',
function() {
// someone got innerHTML
alert('getting innerHTML');
return orig;
});
origObj.__defineSetter__('innerHTML',
function(a) {
// someone set innerHTML
alert('setting innerHTML');
orig = a;
});
} else if(type === "db") {
var orig = origObj.query;
origObj.query = function() {
//someone called query;
alert('calling query');
orig.apply(this, arguments);
};
}
return origObj;
}
var obj = wrapper(document.body);
obj.innerHTML = 'p';
alert(obj.innerHTML);
var db = function() {}
db.query = function() {alert('foo');}
obj = wrapper(db);
obj.query();
edit: "Deleting" answer since it's tagged node.js, leaving it in case it happens to be useful to anyone else:
The general answer is no, it isn't. At least not in every browser, so any solution anyone gives isn't going to work in many cases.
There are a few things that can work, but again there is horrible support for them:
dom modified events (FF only, I believe)
DOMAttrModified
DOMNodeInserted
DOMNodeRemoved
etc
object.watch (FF only)
Looks like I've re-invented the wheel, but somehow this isn't working in Internet Explorer 9, but does in IE6.
function debug()
if(!window.console) {
window.console = { log: function() { /* do something */ } };
}
console.log.apply(console, arguments);
}
Related:
Apply() question for javascript
F12 Debugger tells me that this "object" (console.log) does not support method 'apply'.
Is it not even recognized as a function?
Any other pointers or ideas?
The second part of an answer I gave recently answers this question too. I don't consider this a duplicate of that one so, for convenience, I'll paste it here:
The console object is not part of any standard and is an extension to the Document Object Model. Like other DOM objects, it is considered a host object and is not required to inherit from Object, nor its methods from Function, like native ECMAScript functions and objects do. This is the reason apply and call are undefined on those methods. In IE 9, most DOM objects were improved to inherit from native ECMAScript types. As the developer tools are considered an extension to IE (albeit, a built-in extension), they clearly didn't receive the same improvements as the rest of the DOM.
For what it's worth, you can still use some Function.prototype methods on console methods with a little bind() magic:
var log = Function.prototype.bind.call(console.log, console);
log.apply(console, ["this", "is", "a", "test"]);
//-> "thisisatest"
So you could fix up all the console methods for IE 9 in the same manner:
if (Function.prototype.bind && window.console && typeof console.log == "object"){
[
"log","info","warn","error","assert","dir","clear","profile","profileEnd"
].forEach(function (method) {
console[method] = this.bind(console[method], console);
}, Function.prototype.call);
}
This replaces the "host" functions with native functions that call the "host" functions. You can get it working in Internet Explorer 8 by including the compatibility implementations for Function.prototype.bind and Array.prototype.forEach in your code, or rewriting the above snippet to incorporate the techniques used by those methods.
See also
console.log typeof is "object" instead of "function" - Microsoft Connect (Live account required)
There is also Paul Irish's way of doing it. It is simpler than some of the answers above, but makes log always output an array (even if only one argument was passed in):
// usage: log('inside coolFunc',this,arguments);
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.console){
console.log( Array.prototype.slice.call(arguments) );
}
};
Several of IE's host object functions aren't really JavaScript functions and so don't have apply or call. (alert, for example.)
So you'll have to do it the hard way:
function debug()
var index;
if(!window.console) {
window.console = { log: function() { /* do something */ } };
}
for (index = 0; index < arguments.length; ++index) {
console.log(arguments[index]);
}
}
I came across the same IE trouble and made a routine for it.
It is not as fancy as all the above implementations, but it works in ALL modern browsers.
I tested it with Firefox (Firebug), IE 7,8,9 Chrome and Opera.
It makes use of the evil EVAL, but you will only want to debug in development.
Afterwards you will replace the code with debug = function () {};
So here it is.
Regards, Hans
(function(ns) {
var msgs = [];
// IE compatiblity
function argtoarr (args,from) {
var a = [];
for (var i = from || 0; i<args.length; i++) a.push(args[i]);
return a;
}
function log(arg) {
var params = "", format = "", type , output,
types = {
"number" : "%d",
"object" : "{%o}",
"array" : "[%o]"
};
for (var i=0; i<arg.length; i++) {
params += (params ? "," : "")+"arg["+i+"]";
type = types[toType(arg[i])] || "%s";
if (type === "%d" && parseFloat(arg[i]) == parseInt(arg[i], 10)) type = "%f";
format += (format ? "," : "")+type;
}
// opera does not support string format, so leave it out
output = "console.log("+(window.opera ? "" : "'%f',".replace("%f",format))+"%p);".replace("%p",params);
eval(output);
}
ns.debug = function () {
msgs.push(argtoarr(arguments));
if (console !== undefined) while (msgs.length>0) log(msgs.shift());
}
})(window);
Oops forgot my toType function, here it is.
function toType(obj) {
if (obj === undefined) return "undefined";
if (obj === null) return "null";
var m = obj.constructor;
if (!m) return "window";
m = m.toString().match(/(?:function|\[object)\s*([a-z|A-Z|0-9|_|#]*)/);
return m[1].toLowerCase();
}
Ok, it works when you write it this way:
function debug()
if(!window.console) {
window.console = {};
console.log = function() { /* do something */ };
}
console.log.apply(console, arguments);
}
Odd behaviour... but if you write it this way 'console.log' gets recognized as a function.
The reason I came to this question was that I as trying to 'spicy' the console.log function for a specific module, so I'd have more localized and insightful debug info by playing a bit with the arguments, IE 9 broke it.
#Andy E answer is great and helped me with lots of insight about apply. I just don't take the same approach to support IE9, so my solution is running the console only on "modern browsers" (being that modern means whatever browsers that behave the way I expect =)
var C = function() {
var args = Array.prototype.slice.call(arguments);
var console = window.console;
args[0] = "Module X: "+args[0];
if( typeof console == 'object' && console.log && console.log.apply ){
console.log.apply(console, args);
}
};
Try:
function log(type) {
if (typeof console !== 'undefined' && typeof console.log !== 'undefined' &&
console[type] && Function.prototype.bind) {
var log = Function.prototype.bind.call(console[type], console);
log.apply(console, Array.prototype.slice.call(arguments, 1));
}
}
log('info', 'test', 'pass');
log('error', 'test', 'fail');
Works for log, debug, info, warn, error, group or groupEnd.