Flex Builder Linux Alpha 2 on Debian Etch
I went to the Flex User Group http://ria.meetup.com/7/calendar/6862191/?a=wm1_rsvp yesterday because AIR is one of the technologies I’m most excited about at the moment. I was slightly surprised by the demographic, I think I was expecting a crowd of open source developers, much like you’d find at a hack day or geek event but somehow they didn’t seem as excited about the technologies Adobe are beginning to open up as I did. I wonder whether this is because most of the people who use Adobe products are less technical than the average person you might see at other events and therefore less interested in the new coding opportunities Flex and Air bring.
I got the slight impression that some people were just there for the free beer provided by Adobe (although maybe that’s just because I was sitting near the back by the bar) and others (including myself probably) had a feeling of "why should I do Adobe the favour of switching to flex". I think Adobe’s answer might be along the lines of "we’re doing enoughm just look at the numbers and if you don’t want to take advantage of it that’s up to you" so some of the questions seemed slightly hostile, but maybe I’m reading too much into it and applying my views to my impression of everyone else’s!
I was also slightly surprised by some of the attitudes of the speakers who made frequent references to a designer/developer divide saying things like "you’ll only be impressed with that if you are a developer". Maybe the audience for Adobe products does feature this divide but it surprised me nonetheless and I was surprised it was something the speakers wanted to encourage.
Anyway, despite the above observations I did find the event very interesting and am pleased Adobe are moving towards being more open. One question I asked in the Q&A section was "What are the timescales for AIR on Linux". The answer I got back was "soon" and it appears James Ward is as keen to get AIR on Linux as I am as it is his primary desktop too. Still, I pointed out that if Adobe wants to attract open source developers a Linux port would be pretty essential. Let’s hope it happens "soon" as James promised because developing AIR on FlexBuilder on VMWare is very painful! James also suggested I try the Flex Builder Alpha for Linux so here goes…
First install Java. To do this you’ll need to edit your apt sources list to use the non-free repository:
sudo vim /etc/apt/sources.list
Mine looks like this:
deb http://ftp.uk.debian.org/debian/ etch main non-free deb-src http://ftp.uk.debian.org/debian/ etch main non-free deb http://security.debian.org/ etch/updates main contrib deb-src http://security.debian.org/ etch/updates main contrib
Next install Java 5:
sudo apt-get update sudo apt-get install sun-java5-jdk
(You can probably get away with the JRE if you prefer, in which case install sun-java5-jre instead).
You’ll have to agree to the license:
Package configuration
┌───────────────────────┤ Configuring sun-java5-bin ├───────────────────────┐
│ │
│ Operating System Distributor License for Java v1.1 (DLJ) ↑
│ ▮
│ Operating System Distributor License for Java version 1.1 (DLJ) ▒
│ ▒
│ SUN MICROSYSTEMS, INC. ("SUN") IS WILLING TO LICENSE THE JAVA PLATFORM ▒
│ STANDARD EDITION DEVELOPER KIT ("JDK" - THE "SOFTWARE") TO YOU ONLY UPON ▒
│ THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS LICENSE ▒
│ AGREEMENT (THE "AGREEMENT"). PLEASE READ THE AGREEMENT CAREFULLY. BY ▒
│ INSTALLING, USING, OR DISTRIBUTING THIS SOFTWARE, YOU ACCEPT ALL OF THE ▒
│ TERMS OF THE AGREEMENT. ▒
│ ▒
│ 1. DEFINITIONS. "Software" means the code identified above in binary ▒
│ form, any other machine readable materials including, but not ▒
│ limited to, libraries, source files, header files, and data files), ↓
│
│ <Ok>
│ │
└───────────────────────────────────────────────────────────────────────────┘
Test Java is working by typing java -version at the command line. You should see something like this:
$ java -version java version "1.5.0_10" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_10-b03) Java HotSpot(TM) Client VM (build 1.5.0_10-b03, mixed mode, sharing)
Next you’ll need Eclipse 3.3 or higher. Go to http://www.eclipse.org and download the Linux version (I chose Eclipse IDE for Java EE Developers). Extract the files and launch the program:
tar zxfv eclipse-jee-europa-fall2-linux-gtk.tar.gz cd eclipse ./eclipse
You’ll be asked where you want to create a workspace. I stuck with the default of /home/james/workspace. Eventually Eclipse loads. Once you are happy it is working you should exit it and you can finally you can get around to actually installing Flex Builder.
Download the alpha 2 from the Adobe website: http://labs.adobe.com/downloads/flexbuilder_linux.html then run these commands:
chmod 755 flexbuilder_linux_install_a2_121807.bin ./flexbuilder_linux_install_a2_121807.bin
You’ll see the following output:
Preparing to install... Extracting the installation resources from the installer archive... Configuring the installer for this system's environment... Launching installer...
Then the installer launches and after a few screens you are asked to choose your eclipse folder. This will be the location of the eclipse folder you just extracted. You’ll also be asked to install Flash Player 9 and you should choose to do so, even if you already have it because this version has debugging built-in.
The installation finishes with some errors which aren’t displayed. Instead you are asked to view the installation log but not told where this is and I couldn’t find it! The installer doesn’t tell you how to launch Flex Builder either, but it turns out you just load Eclipse again and Choose Help->Flex Builder whereupon you are asked to enter a license key and told you only have 65 days remaining. After all that effort it would have been nice to know this was only a 65 day trial if it really is. After clicking OK nothing happened (didn’t seem to be able to connect to flexstart.adobe.com) so I exited and tried again.
To get started you need to create a new project. Chose Project… from the File menu and choose a Flex Builder project. There is no support for AIR or BlazeDS so you can only create Flex applications targeting Flash Player 9 but this seems like a good start.
Here’s a screenshot:
http://jimmyg.org/wp-content/uploads/2008/01/flex_builder_running.png
So I think we can add Debian Etch to the list of platforms where Flex Builder works. Good work Adobe and keep it up!
P.S. Was that Evan Davis I spotted at the event? P.P.S Submit bugs!
YUI Autocomplete AJAX Select Drowdown with ID
The YUI toolkit comes with a very flexible autocomplete control but a common requirement is for an autocomplete control that submits the ID associated with a text value rather than the text value itself, much like a select field submits the option value, not the contents the user selects from a drop down list.
Luckily this is fairly easy to achieve using a forceSelection option, a hidden field, and a custom itemSelectEvent handler.
First setup the imports as described on the YUI AutoComplete page:
<!--CSS file (default YUI Sam Skin) -->
<link type="text/css" rel="stylesheet"
href="http://yui.yahooapis.com/2.3.0/build/autocomplete/assets/skins/sam/autocomplete.css">
<!-- Dependencies -->
<script type="text/javascript"
src="http://yui.yahooapis.com/2.3.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<!-- OPTIONAL: Connection (required only if using XHR DataSource) -->
<script type="text/javascript"
src="http://yui.yahooapis.com/2.3.0/build/connection/connection-min.js"></script>
<!-- OPTIONAL: Animation (required only if enabling animation) -->
<script type="text/javascript"
src="http://yui.yahooapis.com/2.3.0/build/animation/animation-min.js"></script>
<!-- OPTIONAL: External JSON parser from http://www.json.org/ (enables JSON validation) -->
<script type="text/javascript" src="http://www.json.org/json.js"></script>
<!-- Source file -->
<script type="text/javascript"
src="http://yui.yahooapis.com/2.3.0/build/autocomplete/autocomplete-min.js"></script>
Now let’s set up the data structure in a Pylons controller action which the YUI component will access to populate the find as you type select dropdown. You could write similar code for Rails or PHP, it doesn’t have to be Pylons. Notice that the @jsonify decorator converts the Python data structure we return to valid JSON:
@jsonify
def get_data(self):
return {
"ResultSet": {
"totalResultsAvailable":"100",
"totalResultsReturned":2,
"firstResultPosition":1,
"Result": [
{
"ID": "897",
"Title":"foo",
"Summary":"... When foo' is used in connection with bar' it has generally traced...",
"Url":"http:\/\/www.catb.org\/~esr\/jargon\/html\/F\/foo.html",
"ModificationDate":1072684800,
"MimeType":"text\/html"
},
{
"ID": "492",
"Title":"Foo Fighters",
"Summary":"Official site with news, tour dates, discography, store, community, and more.",
"Url":"http:\/\/www.foofighters.com\/",
"ModificationDate":1138521600,
"MimeType":"text\/html"
}
]
}
}
Now we need to write the code to generate the autocomplete control. YUI uses an existing text field as the autocomplete field and this will contain whatever text is looked up. Our application requires the corresponding ID so we need a hidden field to hold that value. The hidden field should have the name you want the ID to be submitted as, the text field can have any name because it doesn’t contain the data the application needs. YUI also needs a container <div> which it populates with the results. Ours is called myContainer.
Here’s some HTML to achive this:
<form action="/fayt/submit">
<p><label for="myInput">Sponsor Name<br /></label></p>
<div id="dashboard_autocomplete" style="clear:both; padding-bottom: 20px; width: 400px;">
<input id="myInput_id" type="hidden" name="myInput_id" />
<input id="myInput" type="text" name="item">
<div id="myContainer"></div>
</div>
<input type="submit" name="submit" value="submit" />
</form>
Now let’s add some JavaScript. This can go after the HTML above:
<script type="text/javascript">
var mySchema = ["ResultSet.Result","Title","ID","Url","ModificationDate"];
var myDataSource = new YAHOO.widget.DS_XHR("/fayt/get_data", mySchema);
myDataSource.responseType = YAHOO.widget.DS_XHR.TYPE_JSON;
var myAutoComp = new YAHOO.widget.AutoComplete("myInput","myContainer", myDataSource);
</script>
The first line sets up a schema, the first item of which specifies where the actual results are in the data structrue returned (in this case they are in the Result list of the ResultSet dictionary), the subsequent entries specify values within each result which might be displayed in the control.
Test this example by entering foo into the field. It works as expected returning two options and allowing you to select one but the hidden myInput_id field doesn’t get populated.
To populate the hidden field we need to create a callback function and subscribe it to the autocomplete control’s itemSelectEvent. Add the following code at the end of the JavaScript above, just before the </script> tag:
function fnCallback(e, args) {
YAHOO.util.Dom.get("myInput_id").value = args[2][1];
}
myAutoComp.itemSelectEvent.subscribe(fnCallback);
Now, when an item is selected from the autocomplete control, fnCallback gets called with two arguments. The first is an event object e and the second is a list of arguments described here. These are:
- oSelf
- <YAHOO.widget.AutoComplete> The AutoComplete instance.
- elItem
- <HTMLElement> The selected <li> element item.
- oData
- <Object> The data returned for the item, either as an object, or mapped from the schema into an array.
In this case we want to access the data returned for the item, which we can access as args[2]. Because of the way we set up mySchema earlier the ID field is the second item in the list (you don’t count the first, "ResultSet.Result" because it isn’t one of the data items). We can therefore access the ID of the selected item as args[2][1] (JavaScript arrays are counted from 0 so the second item is numbered 1). All that remains is to assign this ID to the field which is what the YAHOO.util.Dom.get("myInput_id").value = args[2][1]; line does.
Note
If you are adding this HTML and JavaScript to a Mako template in a Pylons application you can replace the URL "/fayt/get_data" with "${h.url_for(controller=’fayt’, action=’get_data’)}" and Pylons will generate the correct URL for you.
Now that the control is working properly let’s write the code to get the ID in a Pylons controller action:
def submit(self):
id = request.params.get('myInput_id')
item = request.params.get('item')
return "The submitted ID was: %s, the selected item was %s" % (id, item)
There are various options you can use to spice up the control. Try adding some of these at the end of the JavaScript, before the </script> tag:
myAutoComp.useShadow = true; myAutoComp.forceSelection = true; myAutoComp.useIFrame = true;
The second of these options ensures that a user selects one of the options from the list rather than entering any old value. This is essential if you want the autocomplete control to be able to replace an actual select control. The third puts the content in an iFrame so that on IE, any form elements beneath the div generated during the autocomplete do not show through.
You can also specify a function to change how the dropdown container formats the infomation. You could use this to display images, make certain parts of the text bold etc. Here’s a simple example:
// This function puts the title in bold if the modification date is
// after 1100000000
myAutoComp.formatResult = function(aResultItem, sQuery) {
var sKey = aResultItem[0]; // the entire result key
// We aren't using these two in this example but it is useful
// to know how to get them.
var sKeyQuery = sKey.substr(0, sQuery.length); // the query itself
var sKeyRemainder = sKey.substr(sQuery.length); // the rest of the result
if (aResultItem[3] > 1100000000) {
var aMarkup = [
"<div id='ysearchresult'>",
"<span style='font-weight:bold'>",
sKey[0],
"</span>",
"</div>"
];
} else {
var aMarkup = [
"<div id='ysearchresult'>",
"<span style='font-weight:normal'>",
sKey[0],
"</span>",
"</div>"
]
}
return (aMarkup.join(""));
};
That’s it. You now have a fully customisable autocomplete control which can be used to replace ordinary HTML <select> fields in cases where there are too many items to easily list in a select alone.
If you spot any mistakes or have suggestions for improvements please feel free to leave a comment.
ExtJS HtmlEditor Example
ExtJS 1.1 has added a simple WYSIWYG HTML editor. There is an official example and an api reference for the HtmlEditor class but not a simple example of how to turn a simple text area into an HtmlEditor so I thought I’d write one.
You need some imports which you’ll need to adjust depending on where you install ext-1.1:
<script type="text/javascript" src="/ext-1.1/adapter/yui/yui-utilities.js"></script>
<script type=”text/javascript” src=”/ext-1.1/adapter/yui/ext-yui-adapter.js”></script>
<script type=”text/javascript” src=”/ext-1.1/ext-all.js”></script>
<link rel=”stylesheet” type=”text/css” href=”/ext-1.1/resources/css/ext-all.css” />
<link rel=”stylesheet” type=”text/css” href=”/ext-1.1/resources/css/xtheme-aero.css” />
<script type=”text/javascript” src=”/ext-1.1/ext-base.js”></script>
Then somewhere in the head or even the body of the HTML add this:
<script type="text/javascript" language="text/javascript">
Ext.onReady(function(){
var editor = new Ext.form.HtmlEditor({id:’note’});
editor.applyTo(’note’);
})
</script>
Finally you’ll need the form and field which the editor should be applied to:
<form action="/index.py" method="post">
<textarea name=”note” id=”note” cols=”125″ rows=”20″></textarea>
<br />
<input type=”submit” name=”go” value=”Save” />
</form>
YUI for AJAX Replace
Here’s the same example as before but implemented using YUI rather than scriptaculous:
${h.select(
”TopicSet-1.TopicID”,
option_tags=topic_options,
onchange=”callAJAX(’%s’,'diseases-select’); return false;”%(
h.url_for(controller=”newstudy”,
action=”disease_dropdown_fragment”),
)
)}
<script type=”text/javascript”>
//<![CDATA[
function callAJAX(sUrl, replace){
var callback = {
success: function(o) {
YAHOO.util.Dom.get(replace).innerHTML = o.responseText;
},
failure: function(o) {
alert(”Failed to retrieve required information.”);
}
}
sUrl = sUrl +’?TopicSet-1.TopicID=’
sUrl = sURL+YAHOO.util.Dom.get(’TopicSet-1.TopicID’).value;
var transaction = YAHOO.util.Connect.asyncRequest(’GET’, sUrl,
callback, null);
}
//]]>
</script>
Scriptaculous Pylons Helpers
I haven’t tried to use the scriptaculous helpers much but since I’m writing a book on Pylons I thought I better give them a go.
Here are three URLs which might help you too:
http://wiki.script.aculo.us/scriptaculous/show/IntegrationWithPylons
http://wiki.pylonshq.com/display/pylonscookbook/Getting+started+with+AJAX
http://workaround.org/pylons/pylons-cheatsheet.html
The bottom line is that before you can really make use of the helpers you need to understand how scriptaculous works on its own first so I’d strongly recommend you don’t touch the helpers until you have some basic examples working. Apart from anything else they seem to do strange things with Mako and escaping which means they probably won’t work anyway!
If you do want to have a play you’ll need to add the scripts themselves:
<script src="/javascripts/prototype.js" type="text/javascript"></script>
<script src=”/javascripts/scriptaculous.js” type=”text/javascript”></script>
or if you want to use the helpers:
${ h.javascript_include_tag(builtins=True) }
Then you can write some code like this:
<select id="TopicSet-1.TopicID" name="TopicSet-1.TopicID" onchange="
new Ajax.Updater(
‘diseases-select’,
‘/newstudy/disease_dropdown_fragment’,
{
asynchronous:true,
evalScripts:true,
parameters:{’TopicSet-1.TopicID’: $(’TopicSet-1.TopicID’).value},
}
);
“>
<option value=”" selected=”selected”>Please select…</option>
<option value=”1″>Blood</option>
<option value=”2″>Cardiovascular</option>
<option value=”3″>Congenital Disorders</option>
</select>
In scriptaculous $ is shorthand for document.getElementByID so $(’TopicSet-1.TopicID’) returns the element with the ID TopicSet-1.TopicID. Notice that it is fine to have elements with a . in their id. This is really handy when it comes to using FormEncode where you might want nested data structures of fields.
The call to Ajax.Updater() does an AJAX request to a URL /newstudy/disease_dropdown_fragment which returns some HTML content which replaces the element with id diseases-select.
The controller action looks like this
def disease_dropdown_fragment(self):
set_dropdown_values(c.connection(), c, request)
return render(”/core/derived/disease_dropdown_fragment.mako”)
It just calls a function to get some values then returns the rendered HTML directly. This approach is much quicker and easier than trying to return a JSON data structure and assemble HTML client-side.
The AJAX updator itself is documented on the scriptaculous wiki.
You can also easily add visual effects. For example, to highlight the div we’ve just updated you can add this to the list of options (just before the asynchronous:true, would be fine.
onComplete:function(){ new Effect.Highlight('diseases-select');},
Remeber you need the , at the end though. Here is some more information on visual effects.
That’s just about it, there are other examples of scriptaculous around on the web but my advice is to not try to use the Pylons helpers until you are very confident using the JavaScript directly, otherwise you might get confused.