Trace
A tool for tracing the execution of Adobe Fireworks extensions written in JavaScript.
Install / Use
/learn @fwextensions/TraceREADME
Writing extensions for Adobe Fireworks is fun when everything works, but can be extremely painful when it doesn’t. Unlike modern browsers, the JavaScript engine in Fireworks does not include a debugger. So when an error occurs, Fireworks just displays a dialog saying “An error occurred”, with no information about what the error is or where it might be. Thanks!
One of my Fireworks Cookbook articles explains how to get better error reporting when you have exceptions caused by buggy JavaScript. If your code is doing something like accessing an undeclared variable or trying to call a function that doesn’t exist, the exception thrown by the JS engine includes the path to the .jsf file containing the error and the line on which it occurred. This information makes it a lot easier to find the bug.
But what if the problem is with how you’re using the Fireworks API itself? Fireworks may tell you that “A parameter was incorrect”, but it doesn’t give you get the line number containing the incorrect parameter. So you have to stick alert() calls at multiple points in your code to see how far your script gets. If you have a lot of code (some of my extensions have a few thousand lines), this can be seriously tedious. Using my Fireworks Console extension helps somewhat, since you can see messages getting dumped to the console instead of dismissing dozens of alerts. But inserting all the log calls (and removing them when you’re done) is still a real pain.
The trace() library makes this whole process much easier.
Installing the trace library
To use the trace library, you will need to download and install my Fireworks Console extension. The console panel must be open while a function is being traced, since it loads a console object and the trace() function into the global scope. trace() calls log() to display strings in the console panel.
Using trace
Imagine you’ve written the following (not very useful) script:
function myBuggyFunc(count)
{
var dom = fw.getDocumentDOM();
dom.selectAll();
dom.clipCopy();
dom.deleteSelection();
for (var i = 0; i < count; i++) {
dom.clipPaste();
dom.moveSelectionTo({ x: i * 10, y: i * 10 });
}
}
myBuggyFunc();
It’s supposed to select everything on the current state, copy it, delete it, then paste it multiple times, each time positioning the copy 10px away from the previous copy (like I said, not terribly useful). When you run the code, Fireworks just says “Could not run the script. A parameter was incorrect”. But which API call is wrong?
Instead of manually adding log() calls to the code, we’ll use the trace library to do it for us. First, open the Fireworks Console panel and then add return trace(this); at the top of the function you want to trace. In our example, it would look like this:
function myBuggyFunc(count)
{
return trace(this);
var dom = fw.getDocumentDOM();
dom.selectAll();
dom.clipCopy();
dom.deleteSelection();
for (var i = 0; i < count; i++) {
dom.clipPaste();
dom.moveSelectionTo({ x: i * 10, y: i * 10 });
}
}
myBuggyFunc();
We’ll look at how that line works later, but for now, just try running your script. You should see the following lines in the Fireworks Console panel:
>>> myBuggyFunc: ( count: undefined )
>>> myBuggyFunc: var dom = fw.getDocumentDOM();
>>> myBuggyFunc: dom.selectAll();
>>> myBuggyFunc: dom.clipCopy();
>>> myBuggyFunc: dom.deleteSelection();
Each line of the trace is prefixed with the name of the function you’re tracing, along with any leading whitespace on the line. The first line shows the function’s parameters and the values that were passed in. The count parameter obviously shouldn’t be undefined, so that’s one problem we need to fix.
After the parameter list, each line in the console displays one line of code from the function. You can see that the last line that executed was dom.deleteSelection(), which means that it likely contains a bug. The Extending Fireworks docs say that deleteSelection() has a bFillDeletedArea parameter which is ignored if Fireworks is not in bitmap editing mode. Unfortunately, it’s still required, even if it’s being ignored. (A lot of Fireworks API calls are like that.)
So let’s pass false to deleteSelection() and re-run the command. The output should now look like this:
>>> myBuggyFunc: ( count: undefined )
>>> myBuggyFunc: var dom = fw.getDocumentDOM();
>>> myBuggyFunc: dom.selectAll();
>>> myBuggyFunc: dom.clipCopy();
>>> myBuggyFunc: dom.deleteSelection(false);
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: returns: undefined
So now we’re getting past the deleteSelection() line, but seem to be going right to the end of the function. The trace() call shows that the function is returning undefined. That just means it doesn’t have an explicit return statement, which is fine.
We need to pass something in the count parameter to get the for-loop started. Let’s change the code to look like this:
function myBuggyFunc(count)
{
return trace(this);
var dom = fw.getDocumentDOM();
dom.selectAll();
dom.clipCopy();
dom.deleteSelection(false);
for (var i = 0; i < count; i++) {
dom.clipPaste();
dom.moveSelectionTo({ x: i * 10, y: i * 10 });
}
}
myBuggyFunc(3);
Now the console should look like this:
>>> myBuggyFunc: ( count: 3 )
>>> myBuggyFunc: var dom = fw.getDocumentDOM();
>>> myBuggyFunc: dom.selectAll();
>>> myBuggyFunc: dom.clipCopy();
>>> myBuggyFunc: dom.deleteSelection(false);
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: dom.clipPaste();
>>> myBuggyFunc: dom.moveSelectionTo({x:i * 10, y:i * 10});
At least we can sees that we’re stepping into the for-loop now, and that the clipPaste() call works. But there seems to be a problem with moveSelectionTo(). That function requires two additional parameters, besides the location to which you’re moving the selection. So let’s pass false for those parameters:
function myBuggyFunc(count)
{
return trace(this);
var dom = fw.getDocumentDOM();
dom.selectAll();
dom.clipCopy();
dom.deleteSelection(false);
for (var i = 0; i < count; i++) {
dom.clipPaste();
dom.moveSelectionTo({ x: i * 10, y: i * 10 }, false, false);
}
}
myBuggyFunc(3);
Now the console trace will show that every line is executing correctly:
>>> myBuggyFunc: ( count: 3 )
>>> myBuggyFunc: var dom = fw.getDocumentDOM();
>>> myBuggyFunc: dom.selectAll();
>>> myBuggyFunc: dom.clipCopy();
>>> myBuggyFunc: dom.deleteSelection(false);
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: dom.clipPaste();
>>> myBuggyFunc: dom.moveSelectionTo({x:i * 10, y:i * 10}, false, false);
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: dom.clipPaste();
>>> myBuggyFunc: dom.moveSelectionTo({x:i * 10, y:i * 10}, false, false);
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: dom.clipPaste();
>>> myBuggyFunc: dom.moveSelectionTo({x:i * 10, y:i * 10}, false, false);
>>> myBuggyFunc: returns: undefined
We can see that the loop executes 3 times, which corresponds to the count value that we passed in.
Note that the error that causes execution to halt will not always be on the last line displayed in the console. If the next line is the beginning of a loop or an if statement, then the problem might be with that line, since those statements are displayed only when execution has entered the block. For instance, tracing this poorly written function:
function findFoo()
{
return trace(this);
var i = 0;
while (fw.selection[i].name != "foo") {
i++;
}
return fw.selection[i];
}
displays this in the console:
>>> findFoo: ( )
>>> findFoo: var i = 0;
The problem is not with the var i = 0; line, but with the fw.selection[i].name part of the while loop. If nothing is selected, this expression will throw an exception, which means the line won’t be displayed. So if you can’t find an error in the last line displayed in the console, always check the next one as well.
Once you’ve figured out where the bug is, you can remove the tracing by just deleting the line you added to the top of the function.
Watching variables and properties
Besides tracing each line, you can also watch variables change as your code executes by passing an array of strings to the trace() call. Each string is evaluated after every line of code, and its current result is displayed under the line. For instance, if you changed the trace() call in myBuggyFunc() to this:
return trace(this, ["i"]);
Then the trace output would look like this:
>>> myBuggyFunc: ( count: 3 )
>>> myBuggyFunc: var dom = fw.getDocumentDOM();
>>> myBuggyFunc: i: undefined
>>> myBuggyFunc: dom.selectAll();
>>> myBuggyFunc: i: undefined
>>> myBuggyFunc: dom.clipCopy();
>>> myBuggyFunc: i: undefined
>>> myBuggyFunc: dom.deleteSelection(false);
>>> myBuggyFunc: i: undefined
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: i: 0
>>> myBuggyFunc: dom.clipPaste();
>>> myBuggyFunc: i: 0
>>> myBuggyFunc: dom.moveSelectionTo({x:i * 10, y:i * 10}, false, false);
>>> myBuggyFunc: i: 0
>>> myBuggyFunc: for (var i = 0; i < count; i++) {
>>> myBuggyFunc: i: 1
...
You can see that the variable i starts out undefined, then increments as the script iterates through the loop.
The w
Related Skills
node-connect
344.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
96.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
344.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
344.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
