Friday, April 17, 2009

"Permission Denied" Error in ADL SCORM 1.2 Test Suite

Yesterday I needed to verify that an older release of my Flash-based e-learning course-shell is indeed SCORM conformant -- specifically to the old-but-widely-used SCORM 1.2 standard. This conformance test is done using the ADL SCORM 1.2 Test Suite -- a locally-run web application that simulates the run-time environment of a SCORM-compliant Learning Management System.

Problem
When launching this SCO in the ADL Test Suite in IE7 on Windows XP SP3, a JavaScript error alerts simply "Permission denied". This error happens when the SCO first locates the test suite's SCORM 1.2 API, so the conformance test cannot even start.

Since the test suite is a local web application (using only HTML, JavaScript, and a Java Applet) local web browser security restrictions apply. Because of this, I always copy my test SCOs into a new folder within the Test Suite folder on my C drive to prevent any potential "cross-domain" (cross-local-folder) security issues. But that does not solve this problem.

I noticed something odd when opening-with Notepad the JS file in my SCO that is responsible for finding the API -- Windows opens a curious confirmation box informing me that the internet can be useful but this file could harm my computer:

'Open File - Security Warning' dialog box with 'While files from the internet are useful, this file type can potentially harm your computer. If you do not trust the source, do not open this software.'
This is strange, because when I create a new JS file then open-with Notepad, no such warning appears.

It turns out Windows now flags files as "obtained from the internet" or not.

Solution
This new Windows security restriction is the cause of this "Permission Denied" error message. Internet Explorer now blocks the scripting between downloaded local files and locally-created local files, in the same fashion as its blocking of cross-site-scripting.

The solution is to modify the properties of each individual file in the SCO to remove this "downloaded-from-the-internet" flag. At the very least, do this to the containing HTML file and the JS files it uses. Click the "Unblock" button on the File Properties window:

File Properties window with 'Unblock' button and 'This file came from another computer and might be blocked to help protect this computer.'
Unfortunately there is no apparent way to "Unblock" in bulk -- you must do it on a file-by-file basis.

One More Solution
If you clicked "Unblock" on all of your files but you still get this Permission Denied error, there is one more thing to check -- especially if your SCO's HTML file is published from Flash.

Flash HTML Publish settings by default throw a special HTML comment-block at the top of the HTML file that looks like this:

<!-- saved from url=(0013)about:internet -->

If your SCO contains that comment, IE will behave the same way as the previous case by blocking the scripting between this "saved-from-the-internet" file and the test suite API. Just delete that comment block and you will be good-to-go.

Friday, April 3, 2009

Escaped Newline not properly rendering from XML

When setting a Flash TextField's text to some data that is loaded from an external XML file, I noticed the newline characters defined in my XML file, "\n", were being rendered as the string "\n" rather than an actual line-break.

Problem
Here is a simplified example of this situation:

The XML file
<root test="Line1\nLine2\nLine3"></root>

The ActionScript
var xml:XML = new XML();

xml.load("slash-n.xml");

xml.onLoad = function()
{
   var testAttr:String = this.firstChild.attributes.test;

   trace(testAttr);
   //this traces: Line1\nLine2\nLine3

   trace("Line1\nLine2\nLine3");
   //this traces:
   //Line1
   //Line2
   //Line3

   trace(testAttr.indexOf("\\n"));
   //this traces: 5
};

This example illustrates that the first trace, using the externally-loaded string, actually outputs "\n", whereas the second trace renders the newline characters in the string literal as line-breaks.

The problem here is that the XML class actually escapes slashes it sees by doubling them up, as in "\\n". This behavior is observed in the third trace which shows that "\\n" exists in the string.


Solution
There are two ways to solve this -- a quick way and a better way.

A Quick Solution - ActionScript Change
The quick way to solve this, leaving the XML as-is, is to simply replace "\\n" with "\n" after the XML load:

ActionScript now replaces "\\n" with "\n"
var xml:XML = new XML();

xml.load("slash-n.xml");

xml.onLoad = function()
{
   var testAttr:String = this.firstChild.attributes.test;

   testAttr = testAttr.split("\\n").join("\n");

   trace(testAttr);
   //this now traces:
   //Line1
   //Line2
   //Line3

};

This solves the problem.

But what if our attribute needs to contain other special characters such as a less-than/greater-than signs or double-quotes? Those must be escaped using the entities &lt; &gt and &quot;, and with a lot of those in the XML, this hinders readability and, if the file is being managed by hand, also hinders writability. A better way would be to use a XML CDATA block instead of an XML attribute.

A Better Solution - ActionScript and XML Changes
This solution takes advantage of the CDATA block, whose contents are not parsed during the XML load, thus not requiring us to escape less-than/greater-than signs and double-quotes.

The ActionScript XML class still doubles-up slashes so we will still need to replace "\\n" with "\n", but our XML file will have higher readability and writability.

Before: XML attribute leads to low readability/writability
<root test="&quot;Line1&quot;\n&gt;&gt;Line2\n&lt;Line3&gt;"></root>

After: XML changed for higher readability/writability
<root><![CDATA["Line1"\n>>Line2\n<Line3>]]></root>

ActionScript refers to CDATA block and replaces "\\n"
var xml:XML = new XML();

xml.load("slash-n.xml");

xml.onLoad = function()
{
   var testCdataText:String = this.firstChild.firstChild.nodeValue;

   testCdataText = testCdataText.split("\\n").join("\n");

   trace(testCdataText);
   //this traces:
   //"Line1"
   //>>Line2
   //<Line3>

};

This solves the newline problem while also increasing the readability and writability of the XML.

Wednesday, February 4, 2009

ExternalInterface vs. JS function returning empty-string

Problem
I have a JavaScript function that always returns a string, but in some cases it needs to return an empty-string. I have Flash, using ActionScript 2, call this function using ExternalInterface.call, but surprisingly when the return value of the JavaScript function is empty-string, ExternalInterface.call returns the 4-character string: "null". If using ActionScript 3, the value null is returned instead of the string.

Example
JavaScript:
function test(bln)
{
   return (bln ? "some text" : "");
}

ActionScript:
import flash.external.*;

ExternalInterface.call(
    "alert",
    "'" + ExternalInterface.call("test", true) + "'");
    //alerts: 'some text'

ExternalInterface.call(
    "alert",
    "'" + ExternalInterface.call("test", false) + "'");
    //alerts: 'null'

Solution
The workaround is to detect "null" or null and replace it with empty-string, as in these examples:

ActionScript 2 workaround
import flash.external.*;

var x = ExternalInterface.call("test", false);

if (x == "null")
    x = "";


ExternalInterface.call("alert", "'" + x + "'"); //alerts: ''

ActionScript 3 workaround
import flash.external.*;

var x = ExternalInterface.call("test", false);

if (x == null)
    x = "";


ExternalInterface.call("alert", "'" + x + "'"); //alerts: ''

Thursday, November 20, 2008

ADO Connection Timeout != Command or RecordSet Timeout

Problem
An interesting bit I learned today is that if you need to extend the time limit for an intensive SQL query or procedure executed using ADO, setting only the commandTimeout property of your ADODB.Connection instance is not sufficient. You will see that you have extended the connection time limit, but queries will still time out within the default limit of 30 seconds.

Example
Using VBScript in ASP 3:
set con = createObject("ADODB.Connection")
con.open connectionString
con.commandTimeout = 60

set command = createObject("ADODB.Command")
command.activeConnection = con
command.commandType = adCmdText
command.commandText = sql
command.execute
response.write command.commandTimeout 'Actually 30 (the default).

Solution
You must also set the commandTimeout property on the ADODB.Command or ADODB.Recordset being used. Otherwise those objects will use the default time limit of 30 seconds because they do not inherit the time limit from the associated ADODB.Connection instance.

Example
Using VBScript in ASP 3:
set con = createObject("ADODB.Connection")
con.open connectionString
con.commandTimeout = 60

set command = createObject("ADODB.Command")
command.activeConnection = con
command.commandType = adCmdText
command.commandText = sql
command.commandTimeout = 60
command.execute
response.write command.commandTimeout 'This is now 60 seconds.

See this Microsoft Tech Note for more.

Friday, November 14, 2008

AS3 Tween class randomly stops during animation

I gave the new AS3 Tween class a whirl to perform some simple animation entirely via ActionScript, rather than sharing the responsibilities between ActionScript and the Timeline.

It worked great...most of the time. The rest of the time, the Tweens would randomly decide to stop -- mid-animation -- without throwing any error as to why. "Most of the time" just doesn't cut it.

The Problem
Here is a simplified example of this situation:

package
{
   import flash.display.*;
   import fl.transitions.*;
   import fl.transitions.easing.*;

   public class SomeContainer() extends Sprite
   {
      ...
      public function animate(o:DisplayObject, x2:int, duration:uint)
      {
         var tween:Tween = new Tween(
               o,
               "x",
               Regular.easeOut,
               someDisplayObject.x,
               x2),
               duration);

      }
   }
}

When invoking animate(), the tween works most of the time, but randomly it stops.

There is no warning of this problem in the ActionScript 3 reference manual, but this Adobe Devnet article contains some fine print that reads:

Note: Consider variable scope when using the Tween class. If a tween is created in a function, it is important that the variable's scope exists beyond the function itself. If a tween is stored to a variable of local scope, ActionScript garbage collection removes the tween as the function completes, which will likely be before the tween has even begun.

So the problem with this scenario is that the Flash Garbage Collector does not care whether the function-scoped variable is still working asynchronously or not - it destroys objects simply by reference counts and in this case nothing outside of the function references this Tween instance.

The Solution
To fix this, simply scope the Tween instance to a class-level variable (a private attribute) rather than a function-level variable. For example:

package
{
   import flash.display.*;
   import fl.transitions.*;
   import fl.transitions.easing.*;

   public class SomeContainer() extends Sprite
   {
      ...
      public function animate(o:DisplayObject, x2:int, duration:uint)
      {
         tween = new Tween(
               o,
               "x",
               Regular.easeOut,
               someDisplayObject.x,
               x2),
               duration);
      }

      private var tween:Tween;
   }
}

This way, the garbage collector sees that something outside of the scope of the function still references the Tween instance created within the function, so it does not destroy the instance.

Wednesday, November 5, 2008

Installing CS4 - Flash 10!

As if it weren't enough of a treat to find out today that Barack Obama was elected as our 44th US President, my day got even better because Adobe CS4 just arrived at the office! I know there's no comparison here, but being given so much in one day is overwhelming!

I'm looking forward to playing with all of the new features in Flash 10 including the new z-axis for 3d transformations, the bones tool for creating skeleton-based animations, and the object motion editor (ripped from After Effects) to replace somewhat clunky keyframe motion.

And to turn it up another notch (BAM!) a free Zinc 3.0 upgrade to 3.0.11 was released to include Flash 10 SWF support.

Tuesday, November 4, 2008

Inter-database Communication is Easy in T-SQL

Problem
You need to obtain data from one data source to process and store in another. You don't want your server-side-script to use two connection objects and be the slow middle-man between them, as in the diagram on the left.

Slow with two SQL Server connections and scripting vs. Optimal with only one connection.
Solution
If both data sources are databases on the same MS SQL Server, it's a piece of cake to do optimally with just one connection object and pure T-SQL.

Just do the following:
  1. Set up Security - Add the user of the destination database as a user of the source database with at least db_datareader access.

  2. Prefix foreign tables with Schema - In your SQL that writes to the destination-database, prefix all tables housed in the source(foreign)-database with their "fully qualified" position in the schema to indicate which database they reside in, as illustrated in the example below.

    Example
    Table tblSource is from of the ForeignDB database, but tblDestination is from the active database.
    insert into tblDestination (colA, colB)
    select col1, col2
    from [ForeignDB].dbo.tblSource