#!/usr/bin/perl
while(<>)
{
s/\bfinal//;
s/\b(int|long) (\w+)/ var $2:int/;
s/\bboolean (\w+)/ var $1:Boolean/;
s/\bString (\w+)/ var $1:String/;
s/System.out.println/trace/;
s/ (void|int|String) (\w+\(.*?\))/ function $2:$1/;
print;
}
The Commodore 1541 disk drive is a computer, with a 6502 microprocessor and its own RAM. It talks to the Commodore 64 via a hastily-built proprietary serial variant of the IEEE488 bus.
And it's a pain to emulate.
Luckily, it's a solved problem, more or less, if your chosen programming language is C++ or Java. If you want to do it in, say, ActionScript, then you are out of luck.
...unless you know Perl.
ActionScript, as you may know, has a fuzzy relationship with Java. Its compiler is written in Java. Its VM may very well be based on the JVM. So it is no surprise that ActionScript source is in many ways a cipher of Java.
I wrote a very small Perl script to convert Java source to ActionScript source. It doesn't do a 100% job, but in all things the best is the enemy of the good, and the Burrito Principle holds (80% of the meat is in 20% of the burrito). So this gets me most of the way there, leaving small scraps to deal with (instead of facing a complete and more tedious rewrite).
Showing posts with label Flex. Show all posts
Showing posts with label Flex. Show all posts
Wednesday, March 28, 2012
Friday, May 27, 2011
Main Points from Adobe
So I attended a day-long presentation on Acrobat and Creative Suite 5.5 recently. Here are the primordial take-aways I got out of it:
* mobile is "big" for Adobe (now). Really!
* the typical information worker spends 17 hours a week *creating* content.
* rich media will be 25% of content by 2013.
* HTML5 is not a document format.
^ That's something which occurred to me during the Acrobat-portion of the presentation.
* process improvements and reducing costs are the top priorities in companies.
* process improvement leads to productivity gain.
* reduced costs are achieved via standards, best practices.
* that's what drove Adobe's work on Acrobat X. Editing content, table data, headers, OCR, sharepoint integration, highliter and sticky-notes, PPT to PDF, embedded QT and MPG, commenting tools, forms, digital signatures, document comparison, a macro system, legal and governmental electronic document standards support, et al.
That's it in a nutshell.
Oh yes: Flex/ActionScript content is maybe 10% of Adobe's business.
* mobile is "big" for Adobe (now). Really!
* the typical information worker spends 17 hours a week *creating* content.
* rich media will be 25% of content by 2013.
* HTML5 is not a document format.
^ That's something which occurred to me during the Acrobat-portion of the presentation.
* process improvements and reducing costs are the top priorities in companies.
* process improvement leads to productivity gain.
* reduced costs are achieved via standards, best practices.
* that's what drove Adobe's work on Acrobat X. Editing content, table data, headers, OCR, sharepoint integration, highliter and sticky-notes, PPT to PDF, embedded QT and MPG, commenting tools, forms, digital signatures, document comparison, a macro system, legal and governmental electronic document standards support, et al.
That's it in a nutshell.
Oh yes: Flex/ActionScript content is maybe 10% of Adobe's business.
Wednesday, March 3, 2010
My Data-Only Object Notation Converter
Here's my home-rolled method for serializing a data-only object to PON (Perl Object Notation) or JSON. "Data-Only" means that the object only contains scalars, hashes, and arrays -- no classes.
// to output JSON, pass in a pair of ":" instead of "=>".
private function encodePON( obj:Object, pair:String="=>" ):String
{
if ( obj is String ) return "'" + obj + "'";
if ( obj is Number ) return obj.toString();
var out:String = "";
var ary:Array;
if ( obj is Array )
{
out += "[";
ary = new Array();
for each (var item:Object in obj as Array)
ary.push( encodePON( item ) );
out += ary.join( ", " );
out += "]";
}
else if ( obj is Object )
{
out += "{";
ary = new Array();
for (var key:String in obj)
ary.push( "'" + key + "' " + pair + " " + encodePON( obj[key] ) );
out += ary.join( ", " );
out += "}";
}
return out;
}
Tuesday, July 21, 2009
Drag and Drop: double binding?
We had a minor fracas at work this morning. The burning issue: does Flex 3, in fact, have double binding?
The official answer is no, Flex 3 does not have double binding. But... it seems that in certain cases, doubly-bound behavior is nevertheless present.
For example:
* create a tree, and enable drag and drop behavior
* set your tree's data provider as an ArrayCollection with nested Arrays; in other words, a treelike structure.
* grab a handy data serialization package, such as as3yaml (http://code.google.com/p/as3yaml/), to display the data structure, then run in debug mode.
Then start dragging elements around, and you'll see something natural: as elements change position, the underlying data structure changes. In other words, the data provider is the model, and the Tree is the view... but also the controller. Change the tree, change the underlying data provider.
Acts like double binding. In reality, it's boilerplate code underwiring the drag and drop attributes on things like Tree and DataGrid. Remember: [Bindable] is boilerplate code forcing the view to change when the model changes. And this one sort of seems like the reverse... hence it is very like double binding.
My sample code:
The official answer is no, Flex 3 does not have double binding. But... it seems that in certain cases, doubly-bound behavior is nevertheless present.
For example:
* create a tree, and enable drag and drop behavior
* set your tree's data provider as an ArrayCollection with nested Arrays; in other words, a treelike structure.
* grab a handy data serialization package, such as as3yaml (http://code.google.com/p/as3yaml/), to display the data structure, then run in debug mode.
Then start dragging elements around, and you'll see something natural: as elements change position, the underlying data structure changes. In other words, the data provider is the model, and the Tree is the view... but also the controller. Change the tree, change the underlying data provider.
Acts like double binding. In reality, it's boilerplate code underwiring the drag and drop attributes on things like Tree and DataGrid. Remember: [Bindable] is boilerplate code forcing the view to change when the model changes. And this one sort of seems like the reverse... hence it is very like double binding.
My sample code:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.as3yaml.*;
private var node1:Object =
{
label: 'parent 1',
index: 0,
children:
[
{ label: 'A' }, // , index: 0 },
{ label: 'B' }, // index: 1 },
{ label: 'C' }, // index: 2 }
]
};
private var node2:Object =
{
label: 'parent 2',
index: 1,
children:
[
{ label: 'D' }, //index: 0 },
{ label: 'E' }, //index: 1 },
{ label: 'F' }, //index: 2 }
]
};
private var node3:Object =
{
label: 'parent 3',
index: 1,
children:
[
{ label: 'G' }, //index: 0 },
{ label: 'H' }, //index: 1 },
{ label: 'I' }, //index: 2 }
]
};
[Bindable]
private var mydata:ArrayCollection = new ArrayCollection([node1, node2, node3]);
private function handleCreationComplete():void
{
dump();
}
private function dump():void
{
var s:String = YAML.encode( mydata.source );
trace( s );
}
]]>
</mx:Script>
<mx:Tree id="mytree"
dataProvider="{mydata}"
dragEnabled="true"
dropEnabled="true"
dragComplete="dump()"
editable="true"
x="40" y="40" width="300" height="300"
/>
</mx:Application>
Monday, July 20, 2009
Refresh that ArrayCollection
So I have this ArrayCollection, which looks basically like this:
It's the dataProvider for a DataGrid, like this:
And then, I've got a horizontal slider that sets a value in the ArrayCollection, like this:
So I can slide the slider, and the "num" attribute of the first element in d1 changes, but the display list in the datagrid doesn't change. And so I learned (or perhaps re-learned) a lesson today:
Whenever you change data in a data provider, also call refresh() on it.
Not only does this re-run filters and sorts, it also refreshes the display. So when a single value changes, calling refresh() will update display lists that are bound to the array.
So, the slider's change property should look like this:
I'm actually using these widgets in a web application I'm slowly working on, which has a web of components that interact with each other. It's wargame rules for starship combat for a pencil-and-paper game called Traveller:
http://eaglestone.pocketempires.com/rules/t5/starships/Shipyard.html
[Bindable]
public var d1:ArrayCollection = new ArrayCollection(
[
{ label: 'first', code: 'A', num: 4 },
{ label: 'sec', code: 'B', num: 7 },
{ label: 'tria', code: 'C', num: 9 }
]
);
It's the dataProvider for a DataGrid, like this:
<mx:DataGrid id="dg1"
dataProvider="{d1}"
dragEnabled="true"
dragDrop="handleDragDrop(event)"
dropEnabled="true"
x="320" y="126">
<mx:columns>
<mx:DataGridColumn headerText="Column 1" dataField="label"/>
<mx:DataGridColumn headerText="Column 2" dataField="code"/>
<mx:DataGridColumn headerText="Column 3" dataField="num"/>
</mx:columns>
</mx:DataGrid>
And then, I've got a horizontal slider that sets a value in the ArrayCollection, like this:
<mx:HSlider id="hslider" x="10" y="320" width="302"
minimum="1" maximum="10" snapInterval="1"
change="d1.getItemAt(0).num = this.hslider.value"
labels="{[1,2,3,4,5,6,7,8,9,10]}" />
So I can slide the slider, and the "num" attribute of the first element in d1 changes, but the display list in the datagrid doesn't change. And so I learned (or perhaps re-learned) a lesson today:
Whenever you change data in a data provider, also call refresh() on it.
Not only does this re-run filters and sorts, it also refreshes the display. So when a single value changes, calling refresh() will update display lists that are bound to the array.
So, the slider's change property should look like this:
change="d1.getItemAt(0).num = this.hslider.value; d1.refresh()"
I'm actually using these widgets in a web application I'm slowly working on, which has a web of components that interact with each other. It's wargame rules for starship combat for a pencil-and-paper game called Traveller:
http://eaglestone.pocketempires.com/rules/t5/starships/Shipyard.html
Monday, July 6, 2009
Data Binding is Beautiful
One of THE best things about Flex is its data binding. Back in Java days, you had to write synchronization code for every GUI component backed by a data model. Thanks to Flex, all that boilerplate boredom is GONE. As a result, I can populate data grids with ease. For example, check out this Flex app I built in a few hours yesterday:
http://eaglestone.pocketempires.com/rules/t5/starships/TechChooser.html
http://eaglestone.pocketempires.com/rules/t5/starships/TechChooser.html
Monday, May 11, 2009
Comprehensive Flex Archive Network
We need a repository where individuals can share ActionScript and Flex modules. Basically, a large collection of Flex software and documentation.
Or do we?
Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.
But, perhaps Flex has a higher level of focus. I've noticed that most of the modules we create require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, Flex tends to focus on the applications (or projects) themselves. So it's a higher-level view.
Or do we?
Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.
But, perhaps Flex has a higher level of focus. I've noticed that most of the modules we create require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, Flex tends to focus on the applications (or projects) themselves. So it's a higher-level view.
Thursday, May 7, 2009
Password Strength Meter
http://eaglestone.pocketempires.com/flex/passwordMeter/PasswordMeter.html
I found a good starting example of a password strength meter at http://subeesh.wordpress.com/2008/02/19/flex-password-strength-meter/#comment-69
It's got a nice graphical component, and great regular expression checkers, but the algorithms used to evaluate password strength are unknown to me. I imported his gradient bar into my component, which is simply a VBox which contains the graphics context for the gradient and algorithm. The component also requires a "dataProvider", which is just the TextInput component you wish to evaluate.
The source is here:
http://eaglestone.pocketempires.com/flex/passwordMeter/Password.mxml
I found a good starting example of a password strength meter at http://subeesh.wordpress.com/2008/02/19/flex-password-strength-meter/#comment-69
It's got a nice graphical component, and great regular expression checkers, but the algorithms used to evaluate password strength are unknown to me. I imported his gradient bar into my component, which is simply a VBox which contains the graphics context for the gradient and algorithm. The component also requires a "dataProvider", which is just the TextInput component you wish to evaluate.
The source is here:
http://eaglestone.pocketempires.com/flex/passwordMeter/Password.mxml
Monday, May 4, 2009
ComboBox Mediator
Friday I applied the Mediator pattern to a pair of ComboBoxes.
The Mediator, in short, is an interaction between two objects that's been encapsulated into a third object. A go-between. It's almost like a Decorator that works on two objects instead of one.
We have Combo Boxes to choose your country and state. So when a user picks a country, then the list of states has to be filtered for valid selections. We do this all throughout our code for various custom forms (the forms themselves all vary in significant ways, otherwise we could simply re-use one form).
Instead of cloning our code over into yet another class, I wrote a ComboBoxMediator, which wires two ComboBoxes together in a source-sink relationship. The MXML looks like this:
The class itself is short. It sets up filtering in the province ComboBox and adds a change event listener on the country ComboBox.
One important step that I originally didn't anticipate is when the province box is being refreshed: I have to check to see if the filtered data results in no data at all (in other words, in cases where a chosen country doesn't have province data yet assigned to it). In this case, I set the data provider on the province box to an inline array with only one value: "Not Applicable".
The Mediator, in short, is an interaction between two objects that's been encapsulated into a third object. A go-between. It's almost like a Decorator that works on two objects instead of one.
We have Combo Boxes to choose your country and state. So when a user picks a country, then the list of states has to be filtered for valid selections. We do this all throughout our code for various custom forms (the forms themselves all vary in significant ways, otherwise we could simply re-use one form).
Instead of cloning our code over into yet another class, I wrote a ComboBoxMediator, which wires two ComboBoxes together in a source-sink relationship. The MXML looks like this:
<bgs:ComboBoxMediator
sourceBox="{countryCodeComboBox}"
sourceSelectedValue="{address.countryCode}"
targetBox="{provinceCodeComboBox}"
targetSelectedValue="{address.provinceCode}"
/>
The class itself is short. It sets up filtering in the province ComboBox and adds a change event listener on the country ComboBox.
One important step that I originally didn't anticipate is when the province box is being refreshed: I have to check to see if the filtered data results in no data at all (in other words, in cases where a chosen country doesn't have province data yet assigned to it). In this case, I set the data provider on the province box to an inline array with only one value: "Not Applicable".
Monday, April 6, 2009
Metric Clock
For those who like the bizarre for the sake of the bizarre, a clock which divides the day into 100 hours of 100 'moments' (each is about 9 seconds long).
Check it out here: http://eaglestone.pocketempires.com/bin/metrictime/MetricClock.html
I've added some spaces in the MXML to make it displayable...
Check it out here: http://eaglestone.pocketempires.com/bin/metrictime/MetricClock.html
I've added some spaces in the MXML to make it displayable...
< ?xml version="1.0" encoding="utf-8"?>
< mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="creationCompleteHandler()"
>
< mx:Script>
< ![CDATA[
import flash.display.Sprite;
import flash.utils.Timer;
private var clock:Sprite = new Sprite();
private var hour:Sprite = new Sprite();
private var sec:Sprite = new Sprite();
public function creationCompleteHandler():void
{
clock.graphics.lineStyle(2);
clock.graphics.drawCircle(0,0,100);
// draw tick marks on clock face
var outerRadius:int = 99;
for ( var m:int=0; m<100 data-blogger-escaped-br="" data-blogger-escaped-m=""> {
var innerRadius:int = 96;
clock.graphics.lineStyle(1, 0x000000, 100);
if ( m % 10 == 5 )
{
innerRadius = 94;
clock.graphics.lineStyle(2, 0x000000, 100);
}
var theta:Number = m * 3.6 * Math.PI / 180;
clock.graphics.moveTo(Math.cos( theta ) * outerRadius,
Math.sin( theta ) * outerRadius);
clock.graphics.lineTo(Math.cos( theta ) * innerRadius,
Math.sin( theta ) * innerRadius);
}
myCanvas.move(150,150);
myCanvas.rawChildren.addChild(clock);
myCanvas.rawChildren.addChild(hour);
myCanvas.rawChildren.addChild(sec);
// draw 'hour' hand
hour.graphics.lineStyle(2, 0x000000, 100);
hour.graphics.moveTo(0, 0);
hour.graphics.lineTo(0, -60);
// draw 'second' hand
sec.graphics.lineStyle(0, 0x0ff00f, 100);
sec.graphics.moveTo(0, 0);
sec.graphics.lineTo(0, -90);
var timer:Timer = new Timer(400);
timer.addEventListener(TimerEvent.TIMER, tick);
timer.start();
}
public function tick(evt:TimerEvent):void
{
var now:Date = new Date();
var time:Number = now.hours * 3600 + now.minutes * 60 + now.seconds + now.milliseconds / 1000;
var metric:int = time * 100/864;
var hrs:Number = int(metric / 100);
var secs:Number = metric % 100;
hour.rotation = 3.6 * hrs;
sec.rotation = 3.6 * secs;
when.text = "";
if ( hrs < 10 ) when.text = "0";
when.text += hrs + ":";
if ( secs < 10 ) when.text += "0";
when.text += int(secs);
}
]]>
< /mx:Script>
< mx:Canvas id="myCanvas" x="0" y="0" width="200" height="200">
< /mx:Canvas>
< mx:Text id="when" x="131" y="264" text="" width="52"/>
< /mx:Application>
Monday, March 30, 2009
Shared Object gotchas
If you don't understand how something works, bugs will ensue.
Experience is what you get when you don't get what you want.
I've got a zillion of these sayings.
Anyway.
The SharedObject is a Flash-cookie way to cache data locally. If one's not there when you reference it, it's created, automagically, in memory until you flush it to disk.
Here's the fun part. We have persisted data that goes stale regularly, and we want to test our caches to make sure they're fresh. We have a GUID for general tests, but we also just test to see if the cache "exists"... and that's a problem. How do you know a cache "exists" if it's created the first time you look at it?
Our fix is to check the object's size. If its size is 0 (zero), then it was just created and doesn't in fact "exist". Therefore we know to go to the database to fetch our data.
Experience is what you get when you don't get what you want.
I've got a zillion of these sayings.
Anyway.
The SharedObject is a Flash-cookie way to cache data locally. If one's not there when you reference it, it's created, automagically, in memory until you flush it to disk.
Here's the fun part. We have persisted data that goes stale regularly, and we want to test our caches to make sure they're fresh. We have a GUID for general tests, but we also just test to see if the cache "exists"... and that's a problem. How do you know a cache "exists" if it's created the first time you look at it?
Our fix is to check the object's size. If its size is 0 (zero), then it was just created and doesn't in fact "exist". Therefore we know to go to the database to fetch our data.
Tuesday, March 17, 2009
Try/Catch with Shared Objects
I've done a bazillion Try/Catches in Java, so in a way this is not a big deal... but I'm a Flex tyro, so in another way this is a big deal.
We store a lot of data locally, as Shared Objects. This reduces the burden on the server and SQL Server. For the first time, though, some data schema changed, which means locally cached data now fails to load, and therefore throws an error. This was easily fixed, with try/catch.
Problem solved.
We store a lot of data locally, as Shared Objects. This reduces the burden on the server and SQL Server. For the first time, though, some data schema changed, which means locally cached data now fails to load, and therefore throws an error. This was easily fixed, with try/catch.
// let's not assume the data is in good condition...
// if it's corrupted or the schema's been changed
// then we throw an error.
try {
cache[cacheName]
= SharedObject.getLocal(cacheName);
}
catch( err:Error ) {
... flag this data to be re-fetched ...
}
Problem solved.
Monday, March 16, 2009
The Button and the Event
So there's a Button that triggers a complex event. In short, it calls a remote object thru Granite to JBoss, and does some neato stuff.
Problem: users have (accidentally) double-clicked (and even triple-clicked) that Button, resulting in multiple event dispatches, bad data, and some server-side exceptions.
Normally, I'd disable the Button.
Then, when processing is done, I'll re-enable it. Easy, right?
But, in this case, that's not fast enough -- the user can click that button five or six times before the Button's disabled! (I didn't think that was possible, but it is).
OK then, I'll cut it off at the source: first, I removed the ActionScript inside the "click" attribute of the MX element:
...and I put the code into an explicit handler, like this:
Add that handler in the creationComplete() method, and voila!
Note the sneaky call in the handler: as soon as we click the Button, the handler removes itself. Thus, subsequent button clicks fall on deaf ears.
Now to put the handler back. We want it back when the manifest data is handled... we have a handler for that of course. So we have:
How clever of us.
New Problem: That handler, shown above, is invoked by other processes as well. So what really happens is that the event handler gets attached to that Button multiple times, and only removed when the Button is pressed. Aiigh!
The solution is mundane and so old school: instead of manipulating the event listener, we add a Boolean value that the Button's listener uses. If it's false, we go ahead and kick off a manifest creation. Then, back in the ManifestData handler, if the boolean is true then we re-set it.
It's not pretty, it feels a bit kludgy, but it seems to work best.
Problem: users have (accidentally) double-clicked (and even triple-clicked) that Button, resulting in multiple event dispatches, bad data, and some server-side exceptions.
Normally, I'd disable the Button.
button.enabled = false;
Then, when processing is done, I'll re-enable it. Easy, right?
button.enabled = true;
But, in this case, that's not fast enough -- the user can click that button five or six times before the Button's disabled! (I didn't think that was possible, but it is).
OK then, I'll cut it off at the source: first, I removed the ActionScript inside the "click" attribute of the MX element:
< id="bntCreateMan">
...and I put the code into an explicit handler, like this:
// this method is triggered ONLY by clicking
// the "Create Manifest" button
private function
handleCreateNewManifest(event:MouseEvent):void
{
this.btnCreateMan.removeEventListener(
MouseEvent.CLICK,handleCreateNewManifest);
...
createNewManifest();
}
Add that handler in the creationComplete() method, and voila!
Note the sneaky call in the handler: as soon as we click the Button, the handler removes itself. Thus, subsequent button clicks fall on deaf ears.
Now to put the handler back. We want it back when the manifest data is handled... we have a handler for that of course. So we have:
private function
handleManifestData(event:ResultEvent):void
{
(...some code...)
this.btnCreateMan.addEventListener(
MouseEvent.CLICK,handleCreateNewManifest);
}
How clever of us.
New Problem: That handler, shown above, is invoked by other processes as well. So what really happens is that the event handler gets attached to that Button multiple times, and only removed when the Button is pressed. Aiigh!
The solution is mundane and so old school: instead of manipulating the event listener, we add a Boolean value that the Button's listener uses. If it's false, we go ahead and kick off a manifest creation. Then, back in the ManifestData handler, if the boolean is true then we re-set it.
It's not pretty, it feels a bit kludgy, but it seems to work best.
Subscribe to:
Posts (Atom)