Você está na página 1de 12

AS3 Script Injection: Introduction

In AS3 Script Injection, complete and unmodified JavaScript and/or VBScript functions, class objects and
applications are stored inside AS3 files using XML, and are then parsed, sent to the browser, and executed,
allowing Flash and Flex developers to create a robust browser experience without the need to rely on server-side
support scripts.

This tutorial will show how to inject and execute complete JavaScripts and VBScripts into a webpage through
ActionScript 3's ExternalInterface Class. In addition, we will show how to store and modify complete JavaScript
and VBScript scripts directly within AS3 sourcecode, where they may be safely kept until needed. Most of the
techniques here may also be applied to AS2 applications with some minor modifications (which will be discussed).

ActionScript-based Script Injection offers the following benefits to developers:

• Server independence: SWF files may be hosted anywhere, and will simply add their own JavaScript-
support files wherever they need them.
• Script Security: JavaScript and VBScript files are stored within the SWF, and as such are not normally
subject to being read and/or modified without the developers consent.
• Transparency: Properly-written, Injected Scripts exist only during their execution, and then automatically
garbage-collect themselves when they are no longer needed. And since they are executed anonymously,
there's no danger of accidentally overwriting existing scripts on the webpage – unless you want to.
• Runtime Script Modification: Scripts may be modified like strings at runtime to address specific needs,
unlike server-based scripts which are essentially static.
• On-Demand Scripting: Scripts are only injected into webpages when needed, conserving system
resources.
• Compression: lengthy JavaScripts may take advantage of SWF compression: e.g. a 32k JavaScript file is
only 5k when stored inside a SWF.

This is an ideal solution for Flash/Flex developers who need JavaScript to interact with the user's browser, but
might not have full access to the webpage or server that their SWF application is actually hosted on. Flash Ads,
YouTube-style video players, and games that may be hosted across multiple (and possibly unforseen) webpages are
the first things that come to mind, but other possibilities abound.

Additionally, because the JavaScript files are stored within Flash and not externally, they are given a certain
amount of anonymity and protection from being read and/or manipulated by third parties, and may take advantage
of SWF compression.

Finally, because the scripts are inherently attached to Flash and exist as editable data within the AS3 file, they can
be modified at runtime by the Flash application to create custom-tailored solutions based on specific needs,
something that is difficult with generic server- and web-encoded scripting solutions.

Note: ActionScript Script Injection should not be confused with the hacker exploit of the same name, also known
as Cross-Site Scripting or XSS. While the underlying concepts are similar, the implementation, intent and (above
all) security differ greatly. ActionScript-based Script Injection is internal and available only to the Flash developer,
as opposed to Hacker Injection, in which otherwise legitimate URLs are "packed" with executable third-party
JavaScript code and launched at public Flash sites.

In the hands of a legitimate developer, AS3 Script Injection is a powerful tool that blurs the boundaries between
Flash, webpages, the server, and the browser.

AS3 Script Injection: The Basics

Let's begin with a succinct definition of what we are about to do:


"In AS Script Injection, complete and unmodified JavaScript and/or VBScripts are stored inside AS3 files using
XML, and are then parsed and sent to the browser, typically using the ExternalInterface class."

That's all there is to it. Of course, getting it all to actually work is the trick, and that's what this tutorial is all about.

Before we dive in, however, we must first dispel some common misconceptions about the
ExternalInterface class:

1. Flash's ExternalInterface can only call named functions.


2. Called functions must already be on the webpage in <SCRIPT> tags.
3. ExternalInterface only works with global functions.
4. In browsers, ExternalInterface only works with JavaScript .

None of these are true, as we shall soon see:

False: Flash's ExternalInterface can only use named functions, and they must already be on the
webpage inside <SCRIPT> tags.

Nothing could be further from the truth! ExternalInterface works by taking your supplied string and performing a
JavaScript eval() on it, forcing the browser to see the string as a JavaScript function of the same name (if one
exists). It then executes a call() on that function, adding any arguments you supplied.

The first key to script injection is that initial eval() statement; JavaScript 's eval function is far more
powerful than ActionScript's, and will attempt to turn literally any string passed to it into a proper value, object or
function. The only problem is that eval() only interprets a single entity (i.e. a single var, object, or function
name) … send it two or more of these entities and it crashes.

This leads us to the second key element: the fact that JavaScript, like ActionScript, can "wrap" almost any number
of individual entities within a single anonymous function. The eval() will see only this "Wrapper Function" (a
single entity), but will happily interpret everything inside of it. That's dolomite baby!

Because of this, ExternalInterface can not only interact with unnamed functions, it can send them, execute
them, and even get a result from them. Consider the following examples. We'll start with the "traditional" use of
ExternalInterface, and build our way up to an Injected Script complete with Wrapper Function.

Traditionally, ExternalInterface takes a single string to be evaluated as a function name, and any number
of optional arguments (primitives or simple objects), as shown below:

ExternalInterface.call("alert", "foo")

This "normal" form of ExternalInterface executes the JavaScript "alert()" function from Flash, and will
display "foo" as the alert-text. But you can also write it like this, and it will function the exact same way:

ExternalInterface.call("alert('foo')")

The function is still executed because Flash converts the entire command into a string, sends it to the browser, then
performs a JavaScript eval() on the string. The eval() recognizes this as a function, and executes it. Lucky for us,
this also works with unnamed functions, so

ExternalInterface.call("function(){}")
is perfectly valid; the anonymous function will actually get executed in the global namespace once it hits the
browser. Which means that this…

ExternalInterface.call("function(){alert('foo');}")

…is an equally valid way to write ExternalInterface.call("alert", "foo"), since the anonymous
function will get called, and will, in turn, call our alert function. But it gets better! Knowing this trick, there's no
reason we can't tuck TWO alerts inside that anonymous function:

ExternalInterface.call("function(){alert('foo'); alert('bar');}")

...which will trigger both alerts, one after the other. In fact, you can embed just about any series of JavaScript
commands, functions, variable declarations etc inside a single anonymous function and it will execute, as this more
complex example shows:

var js:String = "function(){function myFunc(str){alert(str);};myFunc(Foobar);}";


ExternalInterface.call(js)

Because that example was packed as a single-line string, it's a little hard to read, so I'll explain. When fired off by
call(), we first execute our anonymous wrapper function, which creates a local function called myFunc()
(technically a "method" since it resides within another function), which, when called, shows another alert box,
then finally executes myFunc(). Simple code: powerful implications! Here's that JavaScript again, written out
normally so you can read it:

function(){
function myFunc(str){
alert(str);
};
myFunc('foo');
}

Of course, in order to actually work, all that JavaScript needs to be formatted as a string, which means we either
write it out as a ridiculously long string as above, or we use concatenation and escaping as shown below...

var js:String = "function(){"


js+="function myFunc(str){"
js+="alert(str);};"
js+="myFunc(Foobar);}";
ExternalInterface.call(js)

...which is almost as annoying and confusing. Wouldn't it be nice to be able to write that code out directly inside
Flash, as pure JavaScript, and not have to bother about string conversions?

AS3 Script Injection: Embedding JavaScript in AS3

As I was saying on the previous page, using ExternalInterface with anything more than a few lines of JavaScript
gets very cumbersome. Wouldn't it be nice to have a way to store complex JavaScript directly within an AS3 file?

In AS2, the only way to do this would be to either import the .js file (defeating much of the purpose of script
injection), or to render the javascript using lengthy and complex string concatenation (which we'll get to in a bit).

Lucky for us, AS3 provides an amazingly simple way to support JavaScript (or any other language) as fully
editable, unmodified inline text: the XML var type.

There's no better way to explain this than to show it in action:


import flash.external.ExternalInterface;
var myJavaScript:XML =
<script>
<![CDATA[
function(){
var Foobar = 'foo';
function myFunc(str){
alert(str);
};
myFunc(Foobar);
}
]]>
</script> </div>ExternalInterface.call(myJavaScript);

By wrapping our JavaScript inside an XML variable that consists of a single CDATA node, we can tuck our
JavaScript inside our AS3 file and keep it nice and cozy… and perfectly legible. When executed, the contents of
the CDATA will automatically translate to a simple string consisting of just the JavaScript code. (Incidentally, the
name of the root tag, here called "<script>", is completely arbitrary in these simple examples and is ignored… the
CDATA is what's important, because it tells the interpreter to leave its contents alone).

Welcome to script injection!

Side Note: The good news: JavaScript and ActionScript are kissin' cousins and the AS3 interpreter will correctly
format your JavaScript, including indenting it and color-coding the key terms. Very cool.

What's the bad news? The auto-format indentations will be destroyed if you click the Auto-Format button, even
though it is supposed to be secure inside the CDATA tag. To keep this from happening, do a search on the script for
all TAB chars and replace them with 4 spaces… Auto-Format won't touch them after that.

AS3 Script Injection: Persistence, Parameters, and Return Values

Technically, the "Anonymous Wrapper" function is fire-and-forget: It loads, fires, returns a result, then erases itself
and any variables and functions it created, freeing up memory. To the webpage (and more importantly, the
computer's RAM) it is as if the function never existed. But sometimes you WANT persistence… you want one or
more objects created by the function to remain globally (or otherwise) accessible so that other scripts and functions
may access it.

You can do this in several ways depending on the type of persistent information you wish to create (a variable or a
function), and the destination object that you wish for it to persist in (globally-scoped, or scoped to a particular
object).

To create a globally-scoped variable, simply assign it a value within the Wrapper, and omit the "var" reference:

import flash.external.ExternalInterface;
var myJavaScript:XML=
<script>
<![CDATA[
function(){

// local declaration
var myLocal = 'foo';

// global declaration
myGlobal = 'bar';

}
]]>
</script>;

Because "var" was omitted, JavaScript will attempt to find a namespace for "myGlobal", find none, and will assign
it to the namespace of the Wrapper Function: since the Wrapper is being executed in the global namespace,
myGlobal will take up residence there. The same theory goes for creating global functions, except that they must
be declared anonymously and then assigned:

import flash.external.ExternalInterface;
var myJavaScript :XML =
<script>
<![CDATA[
function(){

// named local function: not persistent


function myLocalFunction(){
alert('foo');
}

// anonymous local function: not persistent


var myOtherLocalFunction = function(){
alert('bar');
}

// No "var": This function will go global!!!


myGlobalFunction = function(){
alert('Yo global dude!');
}
}
]]>
</script>

In general, global assignment is a) only possible with the top level of the Wrapper Function (not from within its
methods or other objects), and b) a terrible idea. Global assignment is considered bad practice because you run a
good chance of accidentally overwriting an existing variable, function or method that happens to share the same
name, particularly if you are working on a complex website with scripts by other designers.

It's a much better idea to follow recent Ajax-inspired conventions and tuck your persistent vars and methods into
existing, known objects in the DOM or within JavaScript namespaces (it's an even better idea to make sure those
namespaces exist before you try writing to them!). Consider the following, which shows how to dynamically add a
function to the Dojo framework (Dojo, by the way, is an excellent open source JavaScript library, and is definitely
worth checking out):

import flash.external.ExternalInterface;
var myJavaScript :XML =
<script>
<![CDATA[
function(){
var snafu = 'You said';
// "Dojo" is a JavaScript library object that was
// created by another script, and exists globally.
// Just to be safe, though, we make sure it exists
// before writing to it, otherwise an exception is
// thrown.
if(Dojo){
Dojo.myVar = 'foo';
Dojo.myFunction = function (str) {
alert(snafu + ":" + str);
}
};
}
]]>
</script>
ExternalInterface.call(myJavaScript);

The function and var will now exist within the Dojo object until explicitly destroyed.

Side Note #1: Note that we've also created what's known as a "Closure Function" pattern: because
Dojo.myFunction() uses the local var "snafu", the value of snafu will be remembered by that function whenever it
is called, even though the original reference gets destroyed when the Wrapper Function terminates. In other words,
we've found a way to maintain a completely protected state for our variable. In a later tutorial, we'll look at Closure
Functions as a way to maintain state even when crossing the strings-only Flash/JavaScript boundary.

Side Note #2: If you are unsure about which namespaces exist at the time your Injector fires off, there's a handy
little AS2 script that actually locates the DOM node where the currently-running SWF Player resides. The Player's
DOM node is guaranteed to be both unique and in existence at the time your Injection occurs, making it a
convenient and safe place to store your injected variables and methods. We'll discuss this script in detail in another
tutorial.

Rule of thumb: Unless you have a specific need for your function or var to persist once the wrapper has been
executed, stick with local scoping (named functions and explicitly-"var"ed variables. If you're not careful, you may
trigger a small memory leak or – far worse – you may accidentally overwrite another globally-scoped function of
the same name which was written by another script.

Although it should be obvious, this method of injecting variables and functions into existing Global/DOM objects
also debunks another common ExternalInterface misconception:

False: ExternalInterface only works with global functions.

Above, we added a new method to the "Dojo" global object. All you have to do is
ExternalInterface.call("Dojo.myFunction", "Hello") and you'll be rewarded with an alert
box saying "You said: Hello". So long as the dot path is correct, you can direct ExternalInterface to any
function within the DOM.

Sending parameters to injected scripts


Anonymous functions are cool and all that, but not much use by themselves. And rather than injecting the script,
then making a second call to execute it with parameters, wouldn't it be nice to send some kind of argument along
with the function? Again, there's no better way to explain this than to show it in action:
import flash.external.ExternalInterface;
var myJavaScript:XML =
<script>
<![CDATA[
function(myFoo){
function myFunc (str){
alert(str);
};
myFunc(myFoo);
}
]]>
</script>
ExternalInterface.call(myJavaScript , "foobar");

The call() will execute, sending "foobar" as an argument to the anonymous function.
Getting Return Values from Injected Scripts
Building on our example, we find that we can not only execute anonymous functions, we can get values back from
them as well:
import flash.external.ExternalInterface;
var myJavaScript :XML =
<script>
<![CDATA[
function(myFoo){
function myFunc (str){
return str.toUpperCase()
};
var anonResult = myFunc(myFoo);
return anonResult;
}
]]>
</script>
var myResult = ExternalInterface.call(myJavaScript , "foobar");
// myResult is "FOOBAR"

As you can see, getting results back from injected scripts is really quite simple; all we have to do is get our
anonymous Wrapper Function to return a value, and ExternalInterface gleefully captures it.

Before moving on to more advanced topics, here's one final JavaScript example which wraps everything we've
learned together. We'll start by creating a global namespace, then add a function to it, and finally call the function
in both the traditional way and by using an injected function that modifies the arguments before making the call.
You can simply cut and paste this entire script into a flash framescript, publish it to SWF (with HTML), and then
execute it from the HTML page that results.

import flash.external.ExternalInterface;
var createNamespace_js :XML =
<script>
<![CDATA[
function(){
// Creates a global namespace called
// "Dojo" if one doesn't already exist.
// Here's one way to do this *safely*.
// If Dojo is a valid object of any type,
// including objects and functions, it will
// be left alone. If it doesn't exist, an
// exception is thrown, trapped, and
// "Dojo" is globally created.
try {
Dojo
} catch(e) {
Dojo = new Object();
}
}
]]>
</script>
var addFunction_js :XML =
<script>
<![CDATA[
function(){
var snafu = 'You said';
var exists = false;
// "Dojo" is a JavaScript library object
// that was created by another script,
// and exists globally.
// Just to be safe, though, we make sure
// it exists before writing to it, otherwise
// an exception is thrown.
try {
Dojo;
exists = true;
} catch(e) {
exists = false;
}

if(exists){
Dojo.myVar = 'foo';
Dojo.myFunction = function (str) {
alert(snafu + ":" + str);
}
};
}
]]>
</script>
var callFunction_js :XML =
<script>
<![CDATA[
function(txt, txt2){
txt = txt + " World, " + txt2;
Dojo.myFunction(txt);
}
]]>
</script>
ExternalInterface.call(createNamespace_js);
ExternalInterface.call(addFunction_js);
ExternalInterface.call("Dojo.myFunction", "Hello");
ExternalInterface.call(callFunction_js, "Hello", "Good to see you!" );

AS3 Script Injection: VBScript

Script Injection (and ExternalInterface in general) is not just limited to JavaScript. Using these same
techniques, specialized Flash applications running under Internet Explorer (only) can take advantage of VBScript
as well, leading us to debunk another common misconception:

False: In Browsers, ExternalInterface only works with JavaScript .

ExternalInterface actually works with any language that supports the NPRuntime API. And yes, for most
browsers this means only JavaScript, since it's the universal browser language... However, if you're not
programming for the general public, and can limit users to IE only, a whole new vista (Vista?) opens up: while not
explicitly documented by Adobe, VBScript functions can also be set and called freely using
ExternalInterface.

For instance, add the following VBScript to a webpage:

<script language="VBScript">
Function myBox(txt)
MsgBox(txt, 1)
End Function
</script>

And it will be executed if you call ExternalInterface.call("myBox", "foo"). Niiiiiice!

As noted previously, VBScript access even works with the Script Injection techniques we just discussed, and yes,
VBScript can be easily embedded into AS3 scripts using the XML technique! But there is a small problem.
VBScript lacks the "Wrapper Function" ability of JavaScript, so there's no direct way to inject and execute
complex scripts like we can with JavaScript.Lucky for us there's an indirect way, and we even get to fall back to
JavaScript to pull it off.

Microsoft, with canny foresight, added a little-known (and poorly-documented) JavaScript command called
execScript() which allows JavaScript (technically Jscript, Microsoft's version of the language) to take just about
any language as a string and interpret it. It runs off the JavaScript window object:

window.execScript('MsgBox("foo", 1)', 'vbscript')

Handy, that! Too bad the online docs for it are so darned vague, or they might have mentioned that entire VBScript
applications can be pumped into Internet Explorer through this little command. And that happens to be just what
we need to do...

Injecting and Executing VBScript into a Webpage from Flash

That JavaScript -Inside-XML trick we learned earlier works for VBScript, too:

var myVBScript:XML =
<vbscript>
<![CDATA[
Function myBox (txt)
myBox = MsgBox(txt, 1)
End Function
]]>
</vbscript>

…but there's a problem. While VBScript does support Classes and Encapsulation, it doesn't support them in a way
that allows us to create a Wrapper Function as we can for JavaScript . This makes script injection a little more
complex, but as I mentioned earlier, IE has a special Jscript function called execScript() that fixes the problem.

The solution is to take the embedded VBScript, and convert it a string-based var that JavaScript can read, then run
execScript() on the var. Carriage returns in VB are significant, so we need to maintain these when we write the
JavaScript var out.

import flash.external.ExternalInterface;

function InjectVB(vbXML):* {
var CRLF = String.fromCharCode(13)+String.fromCharCode(10);
var vb = vbXML.toString();
var vb_arr = vb.split(CRLF);
var jsvb="function(){" + CRLF;
jsvb += " var temp='';" + CRLF;
for (var i = 0; i <vb_arr.length; i++) {
var vbTemp = vb_arr[i];
jsvb+=" temp+=('" + vbTemp + "' + String.fromCharCode(13));" + CRLF;
}
jsvb+= " window.execScript(temp, 'vbscript');" + CRLF;
jsvb+= "}";
trace(jsvb);
ExternalInterface.call(jsvb);
}
InjectVB(myVBScript);<p>

What's happening? We take the XML from the previous example, and convert it to a string. We then convert it to an
Array, splitting it on the carriage return - linefeeds that were in the XML (Mac users would be carriage returns
only, but they can't use VBScript anyways). We then rewrite the array of strings into JavaScript commands. Once
executed, the function above will generate our JavaScript function, which in turn will generate VBScript code once
it hits the browser. Based on the example above, the JavaScript that would be written is:

function(){
var temp='';
temp+=(' Function myBox (txt)' + String.fromCharCode(13));
temp+=(' myBox = MsgBox(txt, 2)' + String.fromCharCode(13));
temp+=(' End Function ' + String.fromCharCode(13));
window.execScript(temp, 'vbscript');
}

Which will, in turn, create the following VBScript function inside the webpage:

Function myBox (txt)


myBox = MsgBox(txt, 1)
End Function

Note that execScript does not return a value, so it's necessary to use TWO ExternalInterface.call
commands if you expect to get one… one call to inject the VBScript into the webpage using execScript, and
one to actually call your function and get a value.

You can now call your myBox function using a normal ExternalInterface.call:

var holyCow = ExternalInterface.call("myBox", "Hey it works!");


// holyCow = 1: Clicked OK
// holyCow = 2: Clicked Cancel or Window Close Box

Complete working AS3 code for this example follows. Note that we also take advantage of a JavaScript alert() to
relay the results once the MsgBox has been fired off:

import flash.external.ExternalInterface;
var myVBScript:XML =
<vbscript>
<![CDATA[
Function myBox (txt)
myBox = MsgBox(txt, 1)
End Function
]]>
</vbscript>
function InjectVB(vbXML):* {
var CRLF = String.fromCharCode(13)+String.fromCharCode(10);
var vb = vbXML.toString();
var vb_arr = vb.split(CRLF);
var jsvb="function(){" + CRLF;
jsvb += " var temp='';" + CRLF;
for (var i = 0; i <vb_arr.length; i++) {
var vbTemp = vb_arr[i];
jsvb+=" temp+=('" + vbTemp + "' + String.fromCharCode(13));" + CRLF;
}
jsvb+= " window.execScript(temp, 'vbscript');" + CRLF;
jsvb+= "}";
trace(jsvb);
ExternalInterface.call(jsvb);
}
function alert(txt) {
ExternalInterface.call("alert", txt);
}
function doIt(){
InjectVB(myVBScript);
var holyCow = ExternalInterface.call("myBox", "Hey it works!");
// holyCow = 1: Clicked OK
// holyCow = 2: Clicked Cancel or Window Close Box
alert("You clicked: " + String(holyCow));
}
doIt();

Injection of VBScript isn't as useful as JavaScript, due to the fact that VBScript is only supported on IE. But so
long as you know your users are only browsing with Internet Explorer on Windows, your Flash application will
now have near-complete control over VBScript, allowing you to directly manage ActiveX controls, utilize the very
useful MsgBox procedure, and do other things that VB offers which JavaScript and Flash alone do not.

AS3 Script Injection: Bonus Materials

You now understand the basics of script injection. We have shown that by using a simple mixture of JavaScript,
XML, and AS3, it's very easy to extend the powers of Flash into the world of the webpage.

This concludes the AS3 Script Injection Tutorial.

I leave you with a few useful tidbits that didn't quite fit in anywhere:
For Best Practices I suggest ending all variable names with _js or _vb for JavaScript XML and VBScript
XML, respectively. It would have been nice to create a JavaScript extension to the XML Class, but the XML class
is marked as final so declarations like var script_js:JavaScript are not yet possible. If anybody
knows of a way to pull this off, please let me know!

Injection Starter Script: Here's an empty Wrapper Function for your copy-paste boilerplate enjoyment, consisting
of the import, XML, and calling functions. It's handy to keep around; I for one always forget how to properly
format a CDATA tag!
import flash.external.ExternalInterface;
var script_js :XML =
<script>
<![CDATA[
function(){
// Your Code Here: Dont forget to indent with SPACES
}
]]>
</script>
ExternalInterface.call(script_js);

Which Browser?:
Although by no means the best way to do this, the following script will at least tell you if your SWF file is running
within Internet Explorer, so you'll know if VBScript injection is possible:
import flash.external.ExternalInterface;
var getProp_js :XML =
<script>
<![CDATA[
function(){
return ~PROP~;
}
]]>
</script>;

function GetBrowserProperty(myProp:String):String {
// Helper function to explicitly query the browser
// for the specified property value.
// Note that this can cause the browser to crash
// if you try to get too complex of an object:
// keep it to strings and numbers if possible.
var js = getProp_js.toString();
js = js.replace( /~PROP~/g,myProp);
trace(js);
try {
var myresult = ExternalInterface.call(js);
} catch (e) {
myresult=false;
}
return myresult;
}

var _userAgent = String(GetBrowserProperty("navigator.userAgent"));


var _isIE = (_userAgent=="")?false:(_userAgent.indexOf("MSIE")!= -1);
ExternalInterface.call("alert", "Internet Exploder:" + _isIE);

Look for an advanced version of this script in a future article.


For Further Reading:
Googling the term "Bookmarklets" will result in a plethora of premade anonymous Wrapper Functions, most of
which may be stored and injected from ActionScript. Bookmarklets are JavaScript-based URLs ("javascript:")
intended to be added to a user's "Favorites" list; when clicked, a bookmarklet performs some function, one of a
wide variety such as a page layout modification, a search query or data extraction, rather than go to another
webpage like normal Bookmarks do.

For example: Show the cookie for this Site

The bookmarklet's URL is posted below:


javascript:if(document.cookie.length<1){alert('No%20cookie%20for%20this%20site.')}else{alert
('Cookie%20for%20this%20site:'+document.cookie)}

Bookmarklets went out of fashion a few years ago because of their (then) limited use, but are regaining popularity
because of renewed interest (and improved capabilities) in JavaScript. Most bookmarklets can be used directly
from ActionScript with little or no modification; even if you can't find one that suits your immediate need, studying
them may help you better understand how to write Wrapper-encapsulated functions.

This was my first tutorial. Comments, suggestions, etc are appreciated!

Você também pode gostar