Firebug has the ability to log calls to a particular function name. I'm looking for a bug that sometimes stops a page from rendering, but doesn't cause any errors or warnings. The bug only appears about half the time. So how do I get a list of all the function calls for the entire program, or some kind of stack trace for the execution of the entire program?
Firefox provides console.trace() which is very handy to print the call stack. It is also available in Chrome and IE 11.
Alternatively try something like this:
function print_call_stack() {
var stack = new Error().stack;
console.log("PRINTING CALL STACK");
console.log( stack );
}
When i need a stack trace i do the following, maybe you can draw some inspiration from it:
function logStackTrace(levels) {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox / chrome
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
callstack.push(lines[i]);
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += " at " + lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
if (levels) {
console.log(callstack.slice(0, levels).join('\n'));
}
else {
console.log(callstack.join('\n'));
}
};
Moderator's note: The code in this answer seems to also appear in this post from Eric Wenderlin's blog. The author of this answer claims it as his own code, though, written prior to the blog post linked here. Just for purposes of good-faith, I've added the link to the post and this note.
I accomplished this without firebug. Tested in both chrome and firefox:
console.error("I'm debugging this code.");
Once your program prints that to the console, you can click the little arrow to it to expand the call stack.
Try stepping through your code one line or one function at a time to determine where it stops working correctly. Or make some reasonable guesses and scatter logging statements through your code.
Try this:
console.trace()
I don't know if it's supported on all browsers, so I would check if it exists first.
Related
It's been a long night trying to solve this one.
I'm trying to load a small text file, parse it, then use the information to provide the user with video options. I do this when the page loads but I also do it in response to a user event. In both cases I get the same result. The load_playList function does not execute.
The code is below. The window.load and selectVideo(X) routines are the starting points. In both cases the load_Playlist function is ignored.
It seems that load_playList never executes. The alert message is never executed, yet the script continues as if everything were normal. It's as if I typed the function's name wrong, so I did that and the script failed. So, the browser seems to see the function, but ignores it.
var videoList = [];
var videoTitles = [];
var videoCaptions = [];
/*
var videoList = [
'videos/ZionParkParade.mp4',
'videos/Pointless2014.mp4'];
var videoTitles = [
'The 50GT Zion Canyon Cruise',
'The Tinyvette at Sonoma Raceway'];
var videoCaptions = ['Caption 1','Caption 2'];
*/
window.onload = function()
{
alert(0);
load_playList; // Loads and parses a small text file.
alert(1);
load_video(0); // Set up the first video to play.
alert(2);
}
function load_playList()
{
alert('load_playList');
var listFile = ReadFile('videos/PlayList.txt');
var playList = listFile.split('\n');
var j = 0;
for (i = 0; i < math.trunc(playList.length / 3); i++)
{
videoList[i] = playList[j];
videoTitles[i] = playList[j+1];
videoCaptions[i] = playList[j+2];
j++;
j++;
j++
}
}
function selectVideo(X)
{
alert(10);
load_playList; // Loads and parses a small text file.
alert(11);
load_video(Number(X));
alert(12);
}
function FileRead(U)
{
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
X=new XMLHttpRequest();
}
else
{// code for IE6, IE5
X=new ActiveXObject("Microsoft.XMLHTTP");
}
X.open('GET', U, false );
X.setRequestHeader('Content-Type', 'text/html')
X.send();
return X.responseText;
}
function load_video(N)
{
var V = document.getElementById("video_player");
V.pause();
V.src = videoList[N];
V.auto = false;
V.type = "video/mp4";
// Update the title and captions.
document.getElementById('pause_button').innerHTML = "Play";
document.getElementById('videoTitle').innerHTML = videoTitles[N];
document.getElementById('videoCaption').innerHTML = videoCaptions[N];
}
If I un-comment the initial variable declarations, o provide initial values, everything works, except the text file is never loaded.
Thanks in advance.
Edit - I found two problems in the load_playList routine but still can't get that function to run. I don't even see the first alert.
I pasted the load_playList code into the onload routine and it works. I can live with that, but danged if it makes any sense.
window.onload = function()
{
// load_playList; // Loads and parses a small text file.
var listFile = load_file('videos/PlayList.txt');
var playList = listFile.split('\n');
var j = 0;
for (i = 0; i < Math.trunc(playList.length / 3); i++)
{
videoList[i] = playList[j];
videoTitles[i] = playList[j+1];
videoCaptions[i] = playList[j+2];
j++;
j++;
j++
}
load_video(0); // Set up the first video to play.
var vid = document.getElementById("video_player");
vid.volume = 0.2;
}
function load_playList()
{
alert(10);
var listFile = load_file('videos/PlayList.txt');
alert(11);
var playList = listFile.split('\n');
alert('Length = '+playList.length);
alert('Count = '+Math.trunc(playList.length / 3));
var j = 0;
for (i = 0; i < Math.trunc(playList.length / 3); i++)
{
videoList[i] = playList[j];
videoTitles[i] = playList[j+1];
videoCaptions[i] = playList[j+2];
j++;
j++;
j++
}
alert(12);
}
Just include () after the function name. Just calling the name won't run the function.
load_playList();
Refer
Also refer the fiddle for watching a function call
The work-around, pasting that routine's code into the onload routine, worked, but I wasn't satisfied and finally stumbled on this as a solution:
window.onload = function()
{
load_playList(0); // Loads and parses a small text file.
load_dropdown(0); // Populate the dropdown menu.
load_video(0); // Set up the first video to play.
var vid = document.getElementById("video_player");
vid.volume = 0.2;
}
Neither load_playList nor load_dropdown need an argument passed to them but I did anyway, and that worked.
I'm not sure why this is so but I'll take it.
I'm not a programmer, neither studying something related to it, but just someone who wants to run a code to make my work life easier.
I need to open 50 tabs. Opening one by one takes so much time because when I click the open button, it shows me the new tab opened and then I have to go back to the original page to open the next one and so on.
After a weekend of doing some research, I found that Google Chrome has a "Console" that can be modified to make a webpage work as you want.
The code that runs to open a tab in this webpage is the following. I've run this code in the console and surprisingly it works:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'), {'ngFindListForm:tblDataTable:0:j_id178':'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
And to open the next tab is:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'),{'ngFindListForm:tblDataTable:1:j_id178':'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
As you see, the "only" part of code that changes is the number between colons (0 and 1).
So, according to my basic-high school-programming skills, I think I can change those number with a For Loop from 0 to 49 (50 tabs). I've tried that like this:
for (i = 0; i < 50; i++) {
param = 'ngFindListForm:tblDataTable:' + i +':j_id178';}
And then using this param something like this:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'),{param:'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
But it's not working. It just makes to open the same page I'm on in a new tab.
Maybe the logic I have figured out how to make this work is totally wrong, but this is why I came here to ask you.
Thanks
move your code inside foreach and use [param] (ES6)
var form = document.getElementById('ngFindListForm');
if (typeof jsfcljs == 'function') {
for (i = 0; i < 50; i++) {
var param = 'ngFindListForm:tblDataTable:' + i + ':j_id178';
jsfcljs(form, {
[param]: 'ngFindListForm:tblDataTable:1:j_id178'
}, '_blank');
}
}
OR
var form = document.getElementById('ngFindListForm');
if (typeof jsfcljs == 'function') {
for (i = 0; i < 50; i++) {
var obj = {};
obj['ngFindListForm:tblDataTable:' + i + ':j_id178'] = 'ngFindListForm:tblDataTable:1:j_id178';
jsfcljs(form, obj, '_blank');
}
}
There are a few ways to do it, the easiest would be to use an object and set the string
function jsfcljs(e, o, t) {
console.log(o);
}
for (let i = 0; i < 5; i++) {
var obj = {}
obj['ngFindListForm:tblDataTable:' + i + ':j_id178'] = 'ngFindListForm:tblDataTable:' + i + ':j_id178'
jsfcljs(document.getElementById('ngFindListForm'), obj, '_blank');
}
<div id="ngFindListForm"></div>
Source:
const package = document.querySelector('td[data-bind="text: packageName"');
if (package.textContent.indexOf('Adaptive') !== -1) {
package.click();
const stacks_tab = document.querySelector('ul[class="tabsExpanded"]').children[5];
stacks_tab.click();
function get_sources() {
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
if (copy === '') {
setTimeout(get_sources, 500);
} else {
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
function copy_sources() {
console.log('running');
hidden.select();
if (!document.execCommand('copy')) {
setTimeout(copy_sources, 500);
} else {
console.log('Sources copied!');
}
}
copy_sources();
}
}
get_sources();
} else {
console.log('There is no Adaptive package in this content.');
}
Line 45 is what isn't working.
That code won't make a lot of sense, but here's the use case:
I'm trying to automate part of my job by injecting some JavaScript into the Chrome DevTools console on our CMS that we use for video content where I work. What the script does is click a few elements, then grabs some file locations and copies them to the clipboard as comma separated values. I had this working just fine before, but I decided to try and make the script better...and now the document.execCommand('copy') is just not working.
As you can see, I use some recursion to continuously select the hidden input value and then I try to copy it, and if it fails, I try again in 500 ms. I also log 'running' to ensure the function is actually running (it is). The execCommand() function keeps returning false every 500ms. BUT, if I type it into the console manually and run it, it returns true and works fine even as the recursive function continues to return false. So for some reason, it won't work in the context of my script, but works totally fine when run manually.
Like I said before, it WAS working programatically before, but I changed some stuff to make the script better and more automated, and it won't work anymore. Here's the code with execCommand() working fine:
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
document.execCommand('copy');
I just tested that code and it still works, and copies the text to the clipboard as intended. The only notable different I see is that in the older code, I run execCommand() in the global context, whereas in the new script, it's in a function context. Could this have something to do with it?
So the solution to this was odd. execCommand() can only be triggered by a user event handler, so what I had to do was attach a click listener to the window, then invoke a click event on the hidden node. Because that triggered a click handler, that made it work!
So I think there is something key to be picked up from this situation I encountered and was hoping some experience could explain it.
When I run this code, it does NOT work:
t5 = "nikolas"+t4;
setInterval(adds,250);
function adds(){
if (t4 < 100){
t4=t4+1;
}
else{
return;
}
};
this does DOES work:
t5 = "nikolas"+t4;
adds(t4);
function adds(a){
if (a < 100){
a=a+1;
setInterval(t4=a,250);
}
else{
return;
}
};
TL;DR: setInterval seems to work inside the if block but not outside. When it works it displays nikolast4 where t4 is an integer that 'ticks' from 1-100 (eg.strong text nikolas0 nikolas1 nikolas2 nikolas3 nikolas4)
Also this code (due to the application I am programming in) is supposed to refresh every 250ms (but take the whole refreshing part with a grain of salt, not totally 100% sure about that).
The code below is fully functionnal, and looks very much like your non-working example.
You can check here :
http://jsbin.com/ofezip/1/edit
So i guess you have an issue with the scope of your variables.
window.onload = function() {
var myOutput = document.createElement("output");
document.body.appendChild(myOutput);
var t4 = 0;
var helloInterval = setInterval(adds,250);
function adds(){
if (t4 < 10){
t4++;
myOutput.value = "hello " + t4;
}
else {
myOutput.value = "goodbye" ;
clearInterval(helloInterval);
return;
}
}
};
I have two frames, the expression running in first frame and calling highlightElements function in another frame. This expression works fine in Firefox:
parent.frames[0].highlightElements(lineNumbers, stringObj);
The highlightElements function (just for sure):
function highlightElements(lineNumbers, stringObj) {
// run through the cycle and highlight them
//for (var ln in lineNumbers) {
var length = lineNumbers.length;
for (var ln=0; ln<length; ln++) {
var elements = $('.no');
//for (var i in elements) {
var el_length = elements.length;
for (var i=0; i<el_length; i++) {
if (parseInt(elements[i].innerHTML) == lineNumbers[ln]) {
var badThing = "yes";
for (var nextElement = elements[i].next();
nextElement.className != '.no'; nextElement = elements[i].next()) {
if (nextElement.innerHTML == stringObj) {
badThing = "no";
nextElement.effect('highlight', {}, 'slow');
scrollIntoView(nextElement);
}
}
if (badThing == "yes") alert("Didn't find the object");
}
}
}
}
But in Chrome it produces the error "Uncaught TypeError: Property 'highlightElement' of object[objectDOMWindow] is not a function".
How to change the expression to make it runnable in Chrome? Thanks
Make sure both frames are under same domain and protocol. Chome blocks javascript access from frames to another if the domains/protocols don't match. If you are working locally, and not under a local domain (i.e. the url is something like file:///C:/etc/etc.html) then it won't work either.