I'm trying to correctly suppress warnings (alerts) in DataTables. The standard behavior of DataTables is to throw a javascript alert when an error occurs; however, this is currently inconvenient for me. I have been trying to convert the warning to a javascript error by
$.fn.dataTableExt.sErrMode = 'throw';
Which works correctly, but this stops the current javascript execution, which is not what I want. So, I wrapped the DataTables operations (init and changes) in a try-catch with no error handling; however, this also halts the javascript execution. (Tested on Chrome and Firefox)
My question is how do I go about getting rid of these errors/alerts for the purposes of debugging? I'm trying to debug other parts of my script, but these alerts keep on getting in the way.
I modified the native alert using this closure function to redirect DataTables warnings to the console.
window.alert = (function() {
var nativeAlert = window.alert;
return function(message) {
window.alert = nativeAlert;
message.indexOf("DataTables warning") === 0 ?
console.warn(message) :
nativeAlert(message);
}
})();
It restores the window.alert to its native function on first trigger. If you don't want it to restore to the original alert, just comment out the window.alert = nativeAlert; line.
NB: This answer applies to dataTables 1.9.x!
For $.fn.dataTableExt.sErrMode the only value there has any importance is "alert". It is "alert" or anything else. sErrMode is handled by the internal dispatcher function _fnLog, in v1.9.2 about line 4575 in media/js/jquery.dataTables.js :
function _fnLog( oSettings, iLevel, sMesg )
{
var sAlert = (oSettings===null) ?
"DataTables warning: "+sMesg :
"DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg;
if ( iLevel === 0 )
{
if ( DataTable.ext.sErrMode == 'alert' )
{
alert( sAlert );
}
else
{
throw new Error(sAlert);
}
return;
}
else if ( window.console && console.log )
{
console.log( sAlert );
}
}
Unfortunelaty, there is no way to override dataTables internal functions, believe me - I have tried, not possible with prototyping or anything else. You can read the author Allan Jardines own comment to that here :
I'm sorry to say that due to how DataTables is constructed at the
moment, it's not possible to override an internal function using
Javascript outside of DataTables scope. This is something that will be
addressed whenever I get around to doing the 2.x series (which might
be a while off!) - but at present you would need to alter the core.
One could think that : Hey, perhaps the iLevel-flag can be changed somewhere in the settings? Again, unfortunately no. iLevel is hardcoded in each internal call to _fnLog.
It is somehow disappointing we have to choose between ugly alerts and completely halt of execution, because an error is thrown. A simply override of window.onerror does not work either. The solution is to modify _fnLog, simply comment out the line where the custom error is thrown :
else
{
// throw new Error(sAlert); <-- comment this line
}
And the execution continues if you have $.fn.dataTableExt.sErrMode = 'throw' (anything else but "alert") and if errors occurs. Even better, one could need those thrown errors in other situations, set a flag outside, like
window.isDebugging = true;
and
else
{
if (!window.isDebugging) throw new Error(sAlert);
}
This is not a "hack" in my opinion, but overruling of a general not avoidable jQuery dataTables behaviour that sometimes is not satisfying. As Allan Jardine himself write in the above link :
Why can't you just modify the source? That's the whole point of open
source :-)
Here's a solution proposed here that's slightly modified and works in v1.10.2 without having to change any vendor files:
$.fn.dataTableExt.sErrMode = "console";
$.fn.dataTableExt.oApi._fnLog = function (oSettings, iLevel, sMesg, tn) {
var sAlert = (oSettings === null)
? "DataTables warning: "+sMesg
: "DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg
;
if (tn) {
sAlert += ". For more information about this error, please see "+
"http://datatables.net/tn/"+tn
;
}
if (iLevel === 0) {
if ($.fn.dataTableExt.sErrMode == "alert") {
alert(sAlert);
} else if ($.fn.dataTableExt.sErrMode == "thow") {
throw sAlert;
} else if ($.fn.dataTableExt.sErrMode == "console") {
console.log(sAlert);
} else if ($.fn.dataTableExt.sErrMode == "mute") {}
return;
} else if (console !== undefined && console.log) {
console.log(sAlert);
}
}
try this:
$.fn.DataTable.ext.oApi._fnLog = function (settings, level, msg, tn) {
msg = 'DataTables warning: ' +
(settings !== null ? 'table id=' + settings.sTableId + ' - ' : '') + msg;
if (tn) {
msg += '. For more information about this error, please see ' +
'http://datatables.net/tn/' + tn;
}
console.log( msg );
};
As of DataTables version 1.10.15, you can set $.fn.dataTableExt.errMode to 'ignore' and it will silently ignore the error messages:
$(document).ready(function () {
$.fn.dataTableExt.errMode = 'ignore';
});
_fnLog DataTables function has the following code :
if ( type == 'alert' ) {
alert( msg );
}
else if ( type == 'throw' ) {
throw new Error(msg);
}
else if ( typeof type == 'function' ) {
type( settings, tn, msg );
}
The default value is 'alert' which is problematic.
You can also set to 'throw'. It will create javascript error, but will not do disturb the user.
'ignore' or any other values will just sliently skip the error.
Let me add my 2 cents to davidkonrad's answer above.
One way of modifying _fnLog function without changing the file is to get reference to that method from Api instance in datatables settings:
$.fn.dataTableSettings[0].oApi._fnLog = function(settings, level, msg, tn) {
// Modified version of _fnLog
}
Hope that this will be helpful for someone.
Related
The following error is occurred when we clicking the sub menus from main menu(Mouse hover).
Help me how to solve this issue.
if (browserLogs) {
browserLogs.forEach(function (log) {
var logLevel = context.config.failTestOnErrorLog.failTestOnErrorLogLevel ? context.config.failTestOnErrorLog.failTestOnErrorLogLevel : 900;
var flag = false;
if (log.level.value > logLevel) { // it's an error log
if (context.config.failTestOnErrorLog.excludeKeywords) {
context.config.failTestOnErrorLog.excludeKeywords.forEach(function (keyword) {
if (log.message.search(keyword) > -1) {
flag = true;
}
});
}
expect(log.level.value > logLevel && flag).toEqualBecause(true, 'Error logs present in console:' + require('util').inspect(log));
}
})
}
As you may know forEach is an array operation but I've frequently seen failures with it mistakenly being called on objects.
Can you check if it is definitely an array by including the following two lines?
console.log(typeof context.config.failTestOnErrorLog.excludeKeywords);
console.log(Array.isArray(context.config.failTestOnErrorLog.excludeKeywords));
It it does turn out to be an object you can still loop over it using the information in this post.
Hope the helps, let me if it doesn't and I can suggest other options. Have seen this error many times.
I get TypeError: Cannot read property 'enabled' of undefined both in the anonymous function (on column 21) and the while loop (on column 15):
var enabled = "unknown";
chrome.runtime.sendMessage({request: "Am I enabled?"},
function(response)
{
enabled = response.enabled;
});
while(enabled == "unknown")
{
// wait
}
I don't typically write Javascript, so I'm not sure what I could be doing wrong here. Searching gives me results like var y = null; log(y.property); which are not this issue at all.
The error comes from this line:
enabled = response.enabled;
because response is undefined.
According to the documentation:
If an error occurs while connecting to the extension, the callback will be called with no arguments and runtime.lastError will be set to the error message.
So, modify your code:
var enabled = "unknown";
chrome.runtime.sendMessage({request: "Am I enabled?"},
function(response)
{
if (!response) {
// TODO: Check runtime.lastError and take an appropriate action
} else {
enabled = response.enabled;
if (enabled) {
// TODO: Do the stuff you were planning to do after the while() loop, call a function, etc.
}
}
});
Short version
Trying to write a debug command that returns the call stack, minus the current position. I thought I'd use:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
Long version
I decided I needed a debug library for my content scripts. Here it is:
debug.js
var debugKeys = {
"level": ["off", "event", "function", "timeouts"],
"detail": ["minimal", "detailed"]
};
var debugState = { "level": "off", "detail": "minimal" };
function debug(options) {
if ("level" in options) {
if (verifyDebugValue("level", options["level"]) == false)
return
}
if ("detail" in options) {
if (verifyDebugValue("detail", options["detail"]) == false)
return
}
console.log(options["msg"]);
}
function verifyDebugValue(lval, rval){
var state = 10; // sufficiently high
for (k in debugKeys[lval]) {
if (debugKeys[lval][k] == rval) {
return true;
}
if (debugKeys[lval][k] == debugState[lval]) { // rval was greater than debug key
return false;
}
}
}
When you using it, you can change the debugState in the code to suit your needs. it is still a work in progress but it works just fine.
To use it from another content script, just load it in the manifest like:
manifest.json
"content_scripts": [
{
"js": ["debug.js", "foobar.js"],
}
],
and then call it like:
debug({"level": "timeouts", "msg": "foobar.js waitOnElement() timeout"});
which generates:
foobar.js waitOnElement() timeout debug.js:17
And there is my problem. At the moment, it is using the console log and so all the debug statements come from the same debug.js line. I'd rather return the calling context. I imagine I need something like:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
You can't avoid mentioning the line in your debug.js, because either using throw (...) or console.log/error(...) your debug.js will be issuing the command.
What you can do, is have some try-catch blocks in your code, then in the catch block pass the error object to your debug function, which will handle it according to its debugState.
In any case, it is not quite clear how you are using your debug library (and why you need to remove the last call from the stack-trace, but you could try something like this:
Split the stack-trace (which is actually a multiline string) into lines.
Isolate the first line (corresponding to the last call) that is not part of the error's message.
Put together a new stack-trace, with the removed line.
E.g.:
function removeLastFromStack(stack, errMsg) {
var firstLines = 'Error: ' + errMsg + '\n';
var restOfStack = stack
.substring(firstLines.length) // <-- skip the error's message
.split('\n') // <-- split into lines
.slice(1) // <-- "slice out" the first line
.join('\n'); // <-- put the rest back together
return firstLines + restOfStack;
}
function myDebug(err) {
/* Based on my `debugState` I should decide what to do with this error.
* E.g. I could ignore it, or print the message only,
* or print the full stack-trace, or alert the user, or whatever */
var oldStack = err.stack;
var newStack = removeLastFromStack(oldStack, err.message);
console.log(newStack);
//or: console.error(newStack);
}
/* Somewhere in your code */
function someFuncThatMayThrowAnErr(errMsg) {
throw new Error(errMsg);
}
try {
someFuncThatMayThrowAnErr('test');
} catch (err) {
myDebug(err);
}
...but I still don't see how removing the last call from the trace would be helpful
Is it possible to influence errors thrown from the JS compiler? In particular, I want to create my own error type NullPointerException and then proxy the built-in errors (such as Error and TypeError) to potentially return my custom exception.
Consider the following simple attempt (yes, they are global variables – but they are supposed to be):
NullPointerException = function (msg) {
this.message = msg;
};
NullPointerException.prototype.toString = function () {
return "NullPointerException: " + this.message;
};
var ProxyTypeError = TypeError;
TypeError = function (msg) {
if (msg.indexOf('null') === -1) {
return new ProxyTypeError(msg);
}
return new NullPointerException(msg);
};
This will work fine for cases like
throw new TypeError('normal error'); // 'TypeError: normal error'
throw new TypeError('null'); // 'NullPointerException: null'
However, it won't work for the scenario I actually want it to work:
var obj = null;
console.log( obj.someMethod() ); // 'Uncaught TypeError: ...'
I am aware that browsers both use different messages and different errors, as well as that it's sketchy to even be wanting to do any of this. However, I'd still be interested if there is any actual solution to this? In the end, the use-case is something like
try {
// ... code ...
} catch( e ) {
if( e instanceof NullPointerException ) {
// handle NPE separately
}
// do something else
}
wherein I do not have access to the catch part, hence my desire to throw the error accordingly.
Is it possible to influence errors thrown from the JS interpreter?
No. However, if you want to throw your own errors you can do that for all exceptions that happened in your own code:
try {
/* some code, possibly foreign */
var obj = null;
obj.someMethod()
} catch (e) {
throw new CustomTypeError(e.msg);
}
but it would be much better to just check your types:
var obj = null;
if (obj == null)
throw new NullPointerException();
else
obj.someMethod();
I grabbed a bit of code to do some paging with jQuery, via Luca Matteis here
Paging Through Records Using jQuery
I've made some edits to the paging script so that I can use the same code to provide paging of different content in different locations on the same site.
For the most part, I think it works, except that I get a jsonObj is undefined error in firebug.
When I use alert(jsonObj.toSource()), I am shown the variables that I am trying to populate, but at the same time, the script dies because of the error.
I can't figure out why I am getting this conflict of 'undefined' and yet I can easily out put the 'undefined' values in an alert. I can even say alert(jsonObj.name), and it will give me that value, but still launch an jsonObj is undefined error.
Here's the code I'm using
var pagedContent = {
data: null
,holder: null
,currentIndex : 0
,init: function(data, holder) {
this.data = data;
this.holder=holder;
this.show(0); // show last
}
,show: function(index) {
var jsonObj = this.data[index];
if(!jsonObj) {
return;
}
var holdSubset='';
for(i=0;i<=4; i++){
jsonObj=this.data[index+i];
this.currentIndex = index;
if(this.holder=='div#firstList'){
var returnedId = jsonObj.id;
var returnedName = jsonObj.name;
var calcScore=this.data[index+i].score/this.data[0].score*100;
var resultInput="<div ' id='"+returnedId+"'><div class='name'>"+returnedName+"</div><div class='score'><div style='width:"+calcScore+"%;'></div></div>";
}
if(this.holder=='div#secondList'){
var name=jsonObj.name;
var city=jsonObj.city;
var region=jsonObj.state;
var resultInput='<li><div>'+name+'</div<div>'+city+'</div><div>'+region+'</div></li>';
}
holdSubset= holdSubset+resultInput;
}
jQuery(this.holder).html('<br/>'+holdSubset);
if(index!=0){
var previous = jQuery("<a>").attr("href","#").click(this.previousHandler).text("< previous");
jQuery(this.holder).append(previous);
}
if(index+i<this.data.length){
var next = jQuery("<a style='float:right;'>").attr("href","#").click(this.nextHandler).text("next >");
jQuery(this.holder).append(next);
}
}
,nextHandler: function() {
pagedContent.show(pagedContent.currentIndex + 5);
return false;
}
,previousHandler: function() {
pagedContent.show(pagedContent.currentIndex - 5);
return false
}
};
I call the function like this
pagedContent.init(json.users.locations, 'div#secondList');
The json looks like this
{"locations" : [ {"id":"21319","name":"Naugatuck American Legion","city":"Ansonia","region":"Connecticut"},{"id":"26614","name":"Studio B789","city":"Acton","region":"Maine"},{"id":"26674","name":"Deering Grange Hall","city":"Bailey Island","region":"Maine"},{"id":"27554","name":"Accu Billiards","city":"Acushnet","region":"Massachusetts"}]}
I may have found the problem with your code:
for(i=0;i<=4; i++){
jsonObj=this.data[index+i];
(...)
When you call show(0) you set index to 0. You expect a fixed number of items in the array (5 in the range [0..4]) but there are only 4 locations in your data.
If you are using console.log to trace the problems in firebug you might find that it is a problem with firebug. Try just running console.log on it's own.
If it is a problem with firebug try updating it. There are some development versions around which might fix the problem.
I had a similar problem and fixed it by doing the above.