tag:blogger.com,1999:blog-64497602755925378352023-11-15T09:25:33.059-08:00Flexing My KerknessI have re-located my blog to.. Kerkness.cakerknesshttp://www.blogger.com/profile/12275084412180536585noreply@blogger.comBlogger59125tag:blogger.com,1999:blog-6449760275592537835.post-4919576299372285402008-11-06T08:17:00.000-08:002008-11-06T08:53:45.856-08:00Creating an ItemRenderer in ActionScriptI'm posting this for my benefit as much as anyone else. Here is a very basic ItemRenderer done in ActionScript. Something you might come across when building Flex/Air applications is errand problems with ItemRenderer's not performing as expected, sucking up lots of memory, or throwing errors. Many times these problems can be solved by building your renderer in ActionScript. Here is the source for a basic item renderer which can be scaled up as needed.<br />
<pre>package my.renderer
{
import my.model.DataModel;
import mx.controls.Label;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.listClasses.ListBase;
import mx.core.UIComponent;
public class ArtListRenderer extends UIComponent implements IListItemRenderer
{
public function ArtListRenderer()
{
super();
}
[Bindable] public var myData:DataModel = new DataModel();
// Internal variable for the property value.
private var _data:Object;
// Make the data property bindable.
[Bindable("dataChange")]
// Define the getter method.
public function get data():Object {
return _data;
}
// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void {
_data = value;
myData = new DataModel();
myData.firstname = value.firstname;
myData.lastname = value.lastname;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
private var hBox:HBox;
private var firstnameLabel:Label;
private var lastnameLabel:Label;
override protected function createChildren():void
{
super.createChildren();
hBox = new HBox();
firstnameLabel = new Label();
lastnameLabel = new Label();
hBox.addChild( firstnameLabel );
hBox.addChild( lastnameLabel );
}
override protected function commitProperties():void
{
super.commitProperties();
hBox.horizontalScrollPolicy = 'off';
hBox.verticalScrollPolicy = 'off';
hBox.percentWidth = 100;
firstnameLabel.text = myData.firstname;
lastnameLabel.text = myData.lastname;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth,unscaledHeight);
hBox.move(0,0);
hBox.setActualSize( (unscaledWidth-4), unscaledHeight);
}
}
}
</pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-69274375223407933682008-10-16T12:52:00.000-07:002008-10-24T07:51:44.749-07:00Get PHP dynamic variables inside FlexLet's say that before you load your flex application you want to pass some dynamic variables from PHP for it to act upon. This can be done pretty easily by slipping the values into PHP's <i>$_GET</i> array and using Flex's <i>ExternalInterface</i> class to get access to them from inside the Flex app.<br />
<br />
Here is an example Flex application which pulls values from the URL Query String.<br />
<br />
<pre><mx:Application creationcomplete="init()" layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function init():void
{
var qstr:String = ExternalInterface.call("window.location.search.substring", 1) as String;
var qarr:Array = qstr.split('&');
var pairs:Array;
for( var i:Number = 0 ; i < qarr.length; i++ ){
pairs = String( qarr[i] ).split('=');
if( pairs[0] == 'myName' ){
myName = pairs[1] as String;
}
}
}
]]>
</mx:Script>
<mx:Label text="{myName}" />
</mx:Application></pre><br />
After you build the application and and Flex Builder has generated the <i>.html</i> file that displays your Flex app you can rename the file <i>.php</i> and then add the following to the top of the file.<br />
<br />
<pre><?php $_GET['myName'] = 'Hi I am Kerk'; ?></pre><br />
Now if you upload your SWF file and PHP file to a server that is running PHP and you open it in your browser you'll see your value from PHP displayed in your Flex app.<br />
<br />
I don't know if that is a clear explanation or not, or if this is the best way to pass dynamic variables at load time into your flex application but it works for me. Comments or alternative solutions welcome.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6449760275592537835.post-40665179620298004612008-10-03T09:23:00.000-07:002008-10-03T09:32:54.275-07:00Force creation of complete ViewStack with creationPolicy when building a form across multiple views : Flex TipHere's a tip which might be useful for some people. Sometimes when building a Flex/Adobe Air interface you may want to have form elements which are built across multiple views of a <i>ViewStack</i>, or <i>TabNavigator</i>. For example I have a contact form which has some rarely used fields contained in a secondary Tab in my form.<br />
<br />
Problem with this approach is that the default value of a Container's <i><b>creationPolicy</b></i> is '<i>auto</i>'. This means child components are only created when they are needed. If your user never looks at the extra tabs/view stack layers then these form elements are not available if you need to get values or set values for them.<br />
<br />
The Solution is to simple set the <b><i>creationPolicy</i></b> for your <i>ViewStack</i> or <i>TabNavigator</i> to '<i>all</i>'. This will ensure that all child elements are created up front and are available when needed regardless if the user looks at them or not.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-82794908381415006602008-10-02T08:13:00.000-07:002008-10-02T08:42:19.796-07:00Flex Tip : Use ObjectUtil for Alpha and Numeric sorting on a DataGridIf you haven't taken the time to look at the features of Flex's <i>ObjectUtil</i> you should. Inside are a few handy methods which I see getting over looked.<br />
<br />
Two of those methods are <i>ObjectUtil</i>.<i>stringCompare</i> and <i>ObjectUtil</i>.<i>numericCompare</i>. These methods make it simple to provide intelegent sorting on <i>DataGrid</i> columns.<br />
<br />
Let's say you have a <i>DataGrid</i> that has one column full of ID# and another column full of people's names including both first and last names. Chances are the default sorting abilities of the <i>DataGrid</i> will not properly sort either column. The ID# would be sorted 1,10,11,12... instead of 1,2,3,4,5... and the column of names would sort by the first name and not the last name.<br />
<br />
Lucky the <i>DataGridColumn</i> allows you to set a custom sort function via the property <i>sortCompareFunction</i>. For example look at these two <i>DataGridColumns</i>.<br />
<br />
<pre><mx:DataGridColumn headerText="ID#" dataField="contactid" sortCompareFunction="sortContactId"/>
<mx:DataGridColumn headerText="Name" dataField="full_name" sortCompareFunction="sortLastName">
<mx:itemRenderer>
<mx:Component>
<mx:Label paddingLeft="5" text="{data.firstname} {data.lastname}" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridGolumn>
</pre><br />
The first column is for the ID# of a contact, the second column uses an <i>itemRenderer</i> and displays both the <i>firstname</i> and <i>lastname</i> for the contact. Each column defines it's own <i>sortCompareFunction</i>. Both functions make use of methods from <i>mx.utils.ObjectUtil</i>.<br />
<br />
<pre>private function sortLastName(obj1:Object,obj2:Object):int
{
var value1:String = (obj1.lastname == '' || obj1.lastname == null) ? null : new String(obj1.lastname);
var value2:String = (obj2.lastname == '' || obj2.lastname == null) ? null : new String(obj2.lastname);
return ObjectUtil.stringCompare( value1, value2, true );
}
private function sortContactId(obj1:Object,obj2:Object):int
{
var value1:Number = (obj1.contactid == '' || obj1.contactid == null) ? null : new Number(obj1.contactid);
var value2:Number = (obj2.contactid == '' || obj2.contactid == null) ? null : new Number(obj2.contactid);
return ObjectUtil.numericCompare( value1, value2 );
}
</pre><br />
These functions are pretty straight forward. <i>sortLastName</i> function uses <i>stringCompare</i> method to compare the <i>lastname</i> from two objects. The flag 'true' is set to make sure that the comparison is case insensitive. The <i>sortContactId</i> function uses the <i>numericCompare</i> method to compare the <i>contactid</i> from two objects.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-42287175755828890312008-10-01T19:21:00.000-07:002008-10-01T19:29:04.681-07:00Flex Component : ButtonPanel puts a button in your panel headerIt's always a joy when you need something and after writing a few lines of code, you have it. Such is the beauty of nice extendable components and the Flex architecture.<br /><br />Today I needed a panel which had a button in the top right corner of the header. Where the 'status' text is normally is. I wanted to put a 'save' button there. One problem, the mx.containers.Panel component does not have a button in the header.<br /><br />Solution. Make a new component which extends all the functions of the Panel and stick a button in the top corner where I want.<br /><br /><a href="http://kerkness.ca/flexexamples/ButtonPanel/ButtonPanelDemo.html">Click here to see a demo</a><a href="http://draft.blogger.com/goog_1222914079825"><br /></a><br /><a href="http://kerkness.ca/flexexamples/ButtonPanel/srcview/index.html">Click here to view the source</a><br /><br />My new ButtonPanel component extends Panel and adds 2 new properties and 1 event.<br /><br /><b>Properties</b><br /><br />buttonLabel : <i>String</i> - Defines the label for the button in the top corner<br />buttonPadding : <i>Number</i> - Defines how much padding to provide in the header<br /><br /><b>Event</b><br /><br />buttonClick : <i>Event</i> - Dispatched when the button is clickedUnknownnoreply@blogger.com2tag:blogger.com,1999:blog-6449760275592537835.post-71664029956887559472008-10-01T14:55:00.000-07:002008-10-01T15:00:18.163-07:00Flex Tip: ArrayCollection replace an item with setItemAtOne thing which might not be clear when working with <i>ArrayCollections</i> in Flex is how to replace an item in the collection. The methods <i>getItemAt</i> and <i>addItemAt</i> are pretty clear in their purpose, but one method which I found myself over looking is the <i>setItemAt</i> method which could be more aptly named <i>replaceItemAt</i> as that is exactly what it does. It replaces an item in the collection.<br />
<br />
So next time you're looking to replace an item in an <i>ArrayCollection</i> and wishing there was a <i>replaceItemAt</i> method, look no further than <a href="http://livedocs.adobe.com/flex/201/langref/mx/collections/ListCollectionView.html#setItemAt%28%29">setItemAt</a>. Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-48393340761945862922008-09-13T11:19:00.000-07:002008-09-13T11:34:42.157-07:00PHP json_encode() and Flex JSON.decode GotchaI came across a little 'gotcha' when trying to get Flex <i><b>JSON.decode</b></i> to properly decode a JSON string I was producing with PHP. <br />
<br />
The one of the elements in the string contained ' <i><b>&quote;</b></i> ' which when being encoded by php and sent as a <i><b>HTTPService</b></i> response back to my Flex application was being converted back into double quotes ( <i><b>"</b></i> ). When Flex <i><b>JSON.decode </b></i>attempted to decode the string it would silently fail. No errors or anything.<br />
<br />
In my case my JSON string was already pretty complicated with lots of variable content so it took a while to figure this out. Hopefully someone else finds this post and saves themselves some time.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-54457441623815921062008-09-06T11:23:00.001-07:002008-09-06T13:33:32.169-07:00PHP trim() function in Actionscript for FlexHere is PHP's trim() function reproduced in Actionscript for use in a Flex application<br /><br /><pre>public function trim(str:String):String<br />{<br /> for(var i:Number = 0; str.charCodeAt(i) < 33; i++); <br /> for(var j:Number = str.length-1; str.charCodeAt(j) < 33; j--);<br /> return str.substring(i, j+1);<br />}</pre><br />UPDATE: Thanks to Almog Kurtser, I now know that Flex has a utility to handle trimming strings. Here is an alternative example<br /><pre>import mx.utils.StringUtil;<br /><br />public function trim(str:String):String<br />{<br /> return StringUtil.trim(str);<br />}</pre><br /><br />Of course it is redundant to actually create a function called trim just to call the method StringUtil.trim() but you get the idea.Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-6449760275592537835.post-78690928131967816172008-09-05T13:09:00.000-07:002008-09-05T13:42:04.213-07:00GmailReader.php : A PHP IMAP Class for Reading your Gmail AccountOne of the requirements I had for a recent project involved keeping track of email communication from multiple Gmail users. My first thought was to simply forward a copy of all incoming and outgoing email to an email account on my server and accessing the email that way. There was one problem with this approach. Gmail only allows you to forward a copy of incoming email somewhere else. Emails composed in Gmail stay in Gmail.<br /><br />My solution: Use Gmail's IMAP capabilities and monitor the accounts using <a href="http://ca3.php.net/manual/en/ref.imap.php">PHP-IMAP</a>. This turned out to be a fairly easy to implement solution but I thought I would post my GmailReader class to save other people some initial headaches of getting things rolling.<br /><br />First of all you'll need to enable IMAP for you Gmail Account. Log into Gmail and goto Settings > Forwarding and POP/IMAP<br /><br />Next you'll need to make sure php is compiled with IMAP enabled. This can be done on ubuntu with the following command and then restart apache for good measure.<br /><br /><pre>sudo apt-get install php5-imap<br />sudo /etc/init.d/apache2 restart</pre><br /><br />In my particular scenario I run a script every so often to look for new emails since the last time I checked. So the class file is structured to check for new emails since a specific date string.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Usage Example</span></span><br /><br /><pre>include_once( 'GmailReader.php' );<br />$gmail = new GmailReader( 'username@gmail.com', 'password' ); <br />$email = $gmail->getEmailSince('Fri, 5 Sep 2008 9:00:00');<br /><br />print_r($email);<br /></pre>The above example will print out an array of all emails ( still in the Inbox ) which arrived no earlier than 9am on the 5th of September.<br /><br />You can also check your sent mail using the following example.<br /><pre>$gmail = new GmailReader( 'username@gmail.com', 'password' );<br />$gmail->openSentMail(); <br />$email = $gmail->getEmailSince('Fri, 5 Sep 2008 9:00:00');</pre>Also, if you use Gmail to apply labels to certain email you can monitor just these emails as well.<br /><pre>$gmail = new GmailReader( 'username@gmail.com', 'password' );<br />$gmail->openMailBox( 'MYLABEL' ); <br />$email = $gmail->getEmailSince('Fri, 5 Sep 2008 9:00:00'); </pre><span style="font-size:130%;"><span style="font-weight: bold;">Here is the full GmailReader Class File.</span></span><br /><pre>class GmailReader<br />{<br />var $mbox;<br /><br />function GmailReader( $user, $pass )<br />{<br /> $this->mbox = imap_open("{imap.gmail.com:993/imap/ssl}INBOX",$user,$pass)<br /> or die("can't connect: " . imap_last_error());<br />}<br /><br />function openSentMail()<br />{<br /> imap_reopen($this->mbox, "{imap.gmail.com:993/imap/ssl}[Gmail]/Sent Mail" )<br /> or die("Failed to open Sent Mail: " . imap_last_error());<br />}<br /><br />function openMailBox($mailbox)<br />{<br /> imap_reopen($this->mbox, "{imap.gmail.com:993/imap/ssl}$mailbox" )<br /> or die("Failed to open $mailbox: " . imap_last_error());<br />}<br /><br />function getMailboxInfo()<br />{<br /> $mc = imap_check($this->mbox);<br /> return $mc;<br />}<br /><br />/**<br /> * $date should be a string<br /> * Example Formats Include:<br /> * Fri, 5 Sep 2008 9:00:00<br /> * Fri, 5 Sep 2008<br /> * 5 Sep 2008<br /> * I am sure other's work, just test them out.<br /> */<br />function getHeadersSince($date)<br />{<br /> $uids = $this->getMessageIdsSinceDate($date);<br /> $messages = array();<br /> foreach( $uids as $k=>$uid )<br /> {<br /> $messages[] = $this->retrieve_header($uid);<br /> }<br /> return $messages;<br />}<br /><br />/**<br /> * $date should be a string<br /> * Example Formats Include:<br /> * Fri, 5 Sep 2008 9:00:00<br /> * Fri, 5 Sep 2008<br /> * 5 Sep 2008<br /> * I am sure other's work, just test them out.<br /> */<br />function getEmailSince($date)<br />{<br /> $uids = $this->getMessageIdsSinceDate($date);<br /> $messages = array();<br /> foreach( $uids as $k=>$uid )<br /> {<br /> $messages[] = $this->retrieve_message($uid);<br /> }<br /> return $messages;<br />}<br /><br />function getMessageIdsSinceDate($date)<br />{<br /> return imap_search( $this->mbox, 'SINCE "'.$date.'"');<br />}<br /><br />function retrieve_header($messageid)<br />{<br /> $message = array();<br /> <br /> $header = imap_header($this->mbox, $messageid);<br /> $structure = imap_fetchstructure($this->mbox, $messageid);<br /><br /> $message['subject'] = $header->subject;<br /> $message['fromaddress'] = $header->fromaddress;<br /> $message['toaddress'] = $header->toaddress;<br /> $message['ccaddress'] = $header->ccaddress;<br /> $message['date'] = $header->date;<br /><br /> return $message; <br />}<br /><br />function retrieve_message($messageid)<br />{<br /> $message = array();<br /> <br /> $header = imap_header($this->mbox, $messageid);<br /> $structure = imap_fetchstructure($this->mbox, $messageid);<br /><br /> $message['subject'] = $header->subject;<br /> $message['fromaddress'] = $header->fromaddress;<br /> $message['toaddress'] = $header->toaddress;<br /> $message['ccaddress'] = $header->ccaddress;<br /> $message['date'] = $header->date;<br /><br /> if ($this->check_type($structure))<br /> {<br /> $message['body'] = imap_fetchbody($this->mbox,$messageid,"1"); ## GET THE BODY OF MULTI-PART MESSAGE<br /> if(!$message['body']) {$message['body'] = '[NO TEXT ENTERED INTO THE MESSAGE]\n\n';}<br /> }<br /> else<br /> {<br /> $message['body'] = imap_body($this->mbox, $messageid);<br /> if(!$message['body']) {$message['body'] = '[NO TEXT ENTERED INTO THE MESSAGE]\n\n';}<br /> }<br /> <br /> return $message;<br />}<br /><br />function check_type($structure) ## CHECK THE TYPE<br />{<br /> if($structure->type == 1)<br /> {<br /> return(true); ## YES THIS IS A MULTI-PART MESSAGE<br /> }<br /> else<br /> {<br /> return(false); ## NO THIS IS NOT A MULTI-PART MESSAGE<br /> }<br />}<br /><br /><br />}<br /><br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-61937202077381832132008-08-29T14:32:00.000-07:002008-09-02T09:56:01.892-07:00Flex : DataGrid ItemRenderer and DoubleClick Oh My !!I love it when you come across those little 'gotchas' when programming. Today I found one and it took a while to figure out a solution/hack so I thought I would post my findings and save someone else a little time.<br /><br /><i><b>Scenario</b></i>: You have a DataGrid which uses an ItemRenderer to display the contents of a cell and you need this DataGrid to respond to DoubleClick events.<br /><br /><i><b>Problem</b></i>: The DoubleClick does not fire when the clicking occures in the white space of the item renderer.<br /><br /><i><b>Solution</b></i>: Enable DoubleClick in the itemRenderer as well and send the event to a dummy event handler.<br /><br />Here's an example to clarify. First a MXML component containing the DataGrid.<br /><br /><pre><mx:Script><br /><![CDATA[<br />import mx.controls.Alert;<br />private function dblClickHandler(event:ListEvent):void<br />{ <br /> Alert.show("Double your fun");<br />}<br />]]></mx:Script><br /><mx:DataGrid id="myGrid" width="100%" dataProvider="{myArrayCollection}"<br />doubleClickEnabled="true" itemDoubleClick="dblClickHandler(event)"><br /> <mx:columns> <br /> <mx:DataGridColumn headerText="Col1" itemRenderer="claire.renderer.myCol" /> <br /> </mx:columns><br /></mx:DataGrid></pre><br />Here is the itemRenderer <i> claire.renderer.myCol</i>. Basically we enable double click and for the doubleClick event and we send the event to a dummy handler <i></i><br /><br /><pre><mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" doubleClickEnabled="true"<br /> doubleClick="dummyClickHandler(event)"><br /><mx:Script><br /><![CDATA[<br />private function dummyClickHandler(event:Event):void<br />{ <br /> // do nothing<br />}<br />]]><br /></mx:Script><br /><mx:Label text="{data.artistName}" fontWeight="bold"/> <br /><mx:Label text="{data.artworkMedium}" fontStyle="italic"/> <br /><mx:Label text="{data.artworkSize}"/><br /></mx:VBox></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-35918905222574712982008-08-20T10:06:00.000-07:002008-08-20T14:04:00.034-07:00Kerkness, A PHP Framework for RIA DevelopmentFor the past seven or eight years I've been developing and re-developing a PHP framework which I use as a backend model for almost all of my professional and personal projects. Currently I'm in the process of finishing a re-built from the ground up version that has been tailored for RIA development.<br /><br />The new version is suitable for supporting the backend functionality of any Ajax or HTTP Request type application or web site. I currently use the framework as a backend for Adobe Flex/Air projects as well as a backend for modular web development.<br /><br />With this new version I am also finally making a concious effort to release the framework in under a <a href="http://www.gnu.org/licenses/">GNU General Public License</a> as published by the Free Software Foundation.<br /><br /><b><span style="font-size:large;">How Kerkness Works </span></b><br /><br />Kerkness is a MVC ( module, view, controller ) framework for php specifically designed to provide back end functionality for dynamic web sites and rich internet applications.<br /><br />The Kerkness framework contains several core modules and class objects that aid developers when building custom modules and web services.<br /><br /><span style="font-weight: bold;font-size:130%;" >Making Requests</span><br /><br />By default the Kerkness framework receives requests via HTTP and returns a JSON formatted response. However it also has the ability to return a response based on a custom designed template such as HTML or XML.<br /><br />A typical request could look like this.<br /><a href="http://kerkness.ca/request.php?kr=pages.home" onclick="window.open(this.href, '_blank'); return false;">http://kerkness.ca/request.php?kr=pages.home</a><br /><br />or like this with clean URLs enabled<br /><a href="http://kerkness.ca/pages.home" onclick="window.open(this.href, '_blank'); return false;">http://kerkness.ca/pages.home</a><br /><br />This request is asking for the <i>' home '</i> view from the <i>' pages '</i> module. When this request is received the Kerkness framework creates a request object and will execute the script <i><b>../kerk/modules/pages/home.php</b></i>. The response is returned as a JSON formatted string or designated HTML template.<br /><br /><span style="font-weight: bold;font-size:130%;" >Request Chain</span><br /><br />Requests can be linked together to create a Request Chain so that multiple requests can be called one after another. Kerkness remains flexible and is able to respond to simple AJAX type requests but can also serve complete web pages showing many dynamic content blocks.<br /><br /><span style="font-weight: bold;font-size:130%;" >Getting Started</span><br /><br /><a href="http://kerkness.wikidot.com/getting-started">Click here for a simple tutorial to get up and running quickly.</a><br /><br /><a href="http://kerkness.wikidot.com/">Click here to see the Kerkness Wiki</a><br /><br />Watch this blog for more information and tutorials as I have time to write them. If you wish to get involved or have questions about the framework please leave me a comment.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-7132982450489094492008-07-31T12:24:00.000-07:002008-07-31T12:57:02.604-07:00Updated Flex Qwerty ComponentI've made some updates and improvements to my <a href="http://kerkness.blogspot.com/2008/02/flex-on-screen-qwerty-keyboard.html">Flex Qwerty Keyboard Component</a>.<br /><br />This version of the component is a little more tailored for a touch screen kiosk and is modled a little after the soft-touch keyboard you see on Apples' iPhone. The main new features are shortcut buttons for typing '.com' and '.ca' (cause I'm Canadian) as well as a 'Tab' button for tabbing through form fields.<br /><br /><a href="http://www.kerkness.ca/flexexamples/QwertyDemo/QwertyDemo.html">You can view a demo of the updated component here.</a><br /><br /><a href="http://www.kerkness.ca/flexexamples/QwertyDemo/srcview/index.html">You can view the source code for the updated component here.</a><br /><br /><span style="font-size:130%;"><b>How to Customize this Component<br /></b></span><br />I've had a few comments and questions about how to customize this component. It should be fairly straight forward to do if all you want to do is add or modify how a button works. Here are instructions on how to add an 'Enter' or 'Return' button to the keyboard.<br /><ol><li>First step is to physically add the button to the keyboard. This can be done by adding a new object to one of the <i><b>keyRow</b></i> arrays in the <i>qwerty.mxml</i> file. Adding the following object to the end of the <i><b>keyRowC</b></i> array will add the button to the end of row 3 of the keyboard.<br /><pre>{label:'Return',w:100}</pre>The property 'w' specifies that the button should be 100 pixels wide. Alternatively you could set the property 'flex' to 'true' which would make the button take up as much available space as is available.<br /><br /></li><li>Next you need to instruct the component on how to handle the click event for this new button. You do this by adding a condition to the switch statement in the function <i><b>handleKeyClick</b></i>(). Add the following condition for the new Return button.<br /><pre>case 'Return': this.inputControl.text += "\n";break;</pre></li><li>Finally we need to modify the function which handles toggling between Upper and Lower case on the keyboard. We don't want our new button to be affected by this toggling so we will adjust this function to bypass our new button.<br /><br />In the function <b><i>toggleUpperLower</i></b>() edit the following line from this...<br /><pre>if ( kidLabel == 'Tab' || kidLabel == 'Delete' ) continue;</pre>to this...<br /><pre>if ( kidLabel == 'Tab' || kidLabel == 'Delete' || kidLabel == 'Return' ) continue;</pre></li></ol>There you go, you should have a functional return button. If you have any problems with this example please let me know in the comments.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-6449760275592537835.post-38476209039041332602008-06-26T14:42:00.000-07:002008-06-26T15:12:13.967-07:00Flex Component : Yet Another Flex File Upload ComponentThere are several Flex upload components floating around and I gave most of them a good look over but I was not able to find one which suited my needs so I decided to make my own. Here it is for you to enjoy.<br /><br /><a href="http://www.kerkness.ca/flexexamples/uploader/UploadExample.html">Click here to view a demo</a><br /><span style="font-size:85%;">(demo lets you upload up to 3 files at a time with a max size of 4 megs each)<br /></span><br /><a href="http://www.kerkness.ca/flexexamples/uploader/srcview/index.html">Click here to view the source</a><br /><br />The usage of the component is pretty straight forward. You can set some properties to define what types of files can be uploaded and you direct the 'upload' towards a server side script. In my demo I send the files to a basic PHP script ( which I've included below ). The script handles one file at a time and is expected to report back either the string 'successful' or an error message which is displayed.<br /><br /><span style="font-weight: bold;font-size:180%;" >Properties</span><br /><br />The following public properties can be set to customize the use of this component<br /><ul><li><span style="font-style: italic;">uploadButtonLabel</span> : String = 'Upload'<br />This is the label used for the 'upload' button.<br /><br /></li><li><span style="font-style: italic;">selectButtonLabel</span> : String = 'Select File(s)'<br />This is the label used for the 'select' button.<br /><br /></li><li><span style="font-style: italic;">removeButtonLabel </span>: String = 'Remove Selected File'<br />This is the label used for the 'remove' button.<br /><br /></li><li><span style="font-style: italic;">maxFileCount </span>: Number = 3<br />This sets the number of files a user is allowed to upload<br /><br /></li><li><span style="font-style: italic;">maxFileSize </span>: Number = 1<br />This sets the maximum file size (in megs) for each individual file. Default is 1 meg.<br /><br /></li><li><span style="font-style: italic;">requestUrl</span> : String = ''<br />This is the URL for your PHP/ASP/ColdFusion/Other script which you will send the uploaded files to. THIS PROPERTY MUST BE SET !<br /><br /></li><li><span style="font-style: italic;">allowOnlyImages </span>: Boolean = false<br />If set to 'true' the user will only be able to select image files<br /><br /></li><li><span style="font-style: italic;">allowOnlyText </span>: Boolean = false<br />If set to 'true' the user will only be able to select text files.<br /><br /></li><li><span style="font-style: italic;">allowAllFiles </span>: Boolean = true<br />If set to 'true' the user can upload images or text files.<br /><br /></li><li><span style="font-style: italic;">barColor </span>: String<br />Allows you to style the progress bar</li></ul><span style="font-size:180%;"><span style="font-weight: bold;">Events</span><br /></span><br />The component has one event 'uploadComplete' which is triggered if all files are uploaded successfully.<br /><br /><span style="font-size:180%;"><span style="font-weight: bold;">The PHP Handler</span><br /></span><br />Following is the source for the simple PHP script which I am using in the demo. In this script I am just confirming that the file was uploaded successfully and then I am deleting it. Since this is a public demo I don't want people filling up my server with unwanted images. In your script you would most likely copy the FILE to a more permanent location and possibly do a little more validation. The script needs to echo the string 'success' or echo an error message.<br /><pre>$hasError = false;<br />foreach( $_FILES as $i=>$file ){<br /> if ( $file['error'] ){<br /> $hasError = true;<br /> }<br /> /**<br /> * Because this is a public example. I am just going to immediately delete<br /> * the file which was uploaded. In a regular application you would copy the file<br /> * from it's temporary location to it's permament location.<br /> */<br /> unlink( $file['tmp_name'] ); <br />}<br /><br />if ( ! $hasError ){<br /> echo('success');<br />} else {<br /> echo('Stick custom error message here');<br />}</pre>Unknownnoreply@blogger.com9tag:blogger.com,1999:blog-6449760275592537835.post-31923341571872660002008-06-20T15:33:00.000-07:002008-06-20T15:44:04.171-07:00Locking Down the Ubuntu Log-In ScreenOne of the requirements I have for a kiosk project is to use a timed login instead of an automatic log in when the server boots. This is required so that some special drivers for the touchscreen have time to load and also gives an admin time to log-in using their profile if need be.<br /><br />When the computer boots the Ubuntu login screen is displayed for 10 seconds before the limited user account is automatically logged in and the kiosk application is automatically launched.<br /><br />The problem with this process is that during this 10 second window someone could change the login session from the default ( I normally use blackbox ) to something else by using the little "options" button in the bottom left corner. ( or at least it's in the bottom left corner of the default Ubuntu login theme 'human' ).<br /><br />If you want to lock down or even just customize the login screen to suit your needs an easy way to do this is to edit the XML file which defines and places the elements on the login window. You can edit the main file for the default human theme with gedit.<br /><pre>cd /usr/share/gdm/themes/Human<br />sudo cp Human.xml Human.xml.backup<br />sudo gedit Human.xml</pre>In my file I just commented out the <item> element which defined the option button. Voila ! No more option button.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6449760275592537835.post-10575348859727712342008-06-04T09:10:00.001-07:002008-06-04T09:17:06.919-07:00Something I need to remember about ItemRenderers and the TileList control<blockquote><span>From a high level perspective, scrolling is moving </span><span>data through fixed <b class="highlight">itemRenderers</b>, not by actually moving renderers <b class="highlight">off</b> the visible area <b class="highlight">of</b> </span><br /><span>the screen.</span></blockquote>I found this little bit of information hidden in <a href="http://www.nabble.com/Re%3A-TileList-and-itemrenderer-customization-p14564260.html">this thread</a>. After trying to extensively use my PageList component in a large application and with a dataProvider that had the potential of providing hundreds of items needing rendering I was noticing a very bad side effect. My application would continue to create a new instance of the itemRenderer for every item in the dataProvider and if the dataProvider was emptied ( ie: arrayCollection.removeAll() ) the itemRenderers would remain.<br /><br />Back to the drawing board on that one. I hope to post a new PageList component soon as my application is not usable in it's current state.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-89352124924042211262008-05-20T13:20:00.001-07:002008-05-21T17:04:21.492-07:00Multiple AutoComplete Input Controls in a single Flex FormA very nice component which is available from Adobe is their <a href="http://www.adobe.com/cfusion/exchange/index.cfm?event=extensionDetail&extid=1047291">AutoComplete ComboBox</a>. The AutoComplete Combo box looks like a regular TextInput component but will provide suggestions to the user as they type. This is very handy when you want to let users select from a long list of options ( such as a list of Countries ).<br /><br />Jen Krause has posted a <a href="http://www.websector.de/blog/2008/04/30/quick-tip-avoid-issues-using-adobes-autocomplete-input-component-using-flex-3/#comment-18368">modified version</a> of the component which allows the user to press ENTER, TAB or click their mouse button to select the first selected item. This is a very nice modification.<br /><br />A problem I ran into however was using multiple AutoComplete components in the same Flex form. If you try and put more than one AutoComplete component in the form, the first component looses the label of the selected item when you begin using the second component. I wasn't able to determine the cause of this problem but I was able to provide a simple work around.<br /><br />If you've run into a same problem, you can fix it by making the following change to the class' focusOutHandler method.<br /><pre>// Change this method ...<br />override protected function focusOutHandler(event:FocusEvent):void<br />{<br />super.focusOutHandler(event)<br />if(keepLocalHistory && dataProvider.length==0)<br /> addToLocalHistory();<br />}<br /><br />// To this ....<br />override protected function focusOutHandler(event:FocusEvent):void<br />{<br />super.focusOutHandler(event)<br />if(keepLocalHistory && dataProvider.length==0)<br /> addToLocalHistory();<br />_typedText = textInput.text; // replace typedText with textInput.text<br />}</pre><br />I'm not sure if there are any ramifications to this fix but I haven't found any additional problems in my own application.<br /><br /><span style="font-weight: bold;">UPDATE<br /></span>Turns out there are numberous implications when using multiple AutoComplete Input controls in the same form specifically when you want to try and set the value of the controls with actionscript. If you only need one AutoComplete input control then this component works really well and is very user friendly. However if you need multiple controls you may want to re-consider your form design choices.<span style="font-weight: bold;"><br /></span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6449760275592537835.post-34512889982117346552008-05-14T07:38:00.001-07:002008-05-14T08:34:32.885-07:00Running Adobe Air for Linux on UbuntuIf you know what an RIA is then you know what Adobe Air is. If you don't know what either of them are, then you really shouldn't need to read this post.<br /><br />I need to find out what type of performance boost their is running a desktop Flex application as an Adobe Air application compared to running it using FireFox with Flash Player 9. So in order to do that I need to make sure I can get Adobe Air for Linux ( currently in Alpha ) working. I need to do this because my application needs to run off a linux desktop environment.<br /><br /><ol><li>First download Adobe Air for Linux runtime environment from the following link. You don't need to install the SDK unless you plan on developing Air applications using a linux desktop. I've started using OSX myself so I'm only going to install the runtime environment.<a href="http://labs.adobe.com/downloads/air_linux.html"><br /><br />http://labs.adobe.com/downloads/air_linux.html<br /><br /></a></li><li>I moved my bin file to /usr/share/air and then changed the permissions.<br /><pre>sudo mkdir /usr/share/air<br />sudo mv adobeair_linux_al_******.bin<br />chmod +x adobeair_linux_al_******.bin</pre><br /><br /></li><li>Then from the desktop go to Places > Computer > usr > share > air and double click on the bin file.<br /><br /></li><li>Follow the prompts and you're done installing the runtime environment.<br /><br /></li><li>Now let's see if we can get a fancy Air Application running. Go to the Sample application section at Adobe Labs and download an application that interests you. I downloaded Signet which gives you the ability to take your del.icio.us bookmarks to the desktop (whatever that means).<br /><br /><a href="http://labs.adobe.com/technologies/air/samples/">http://labs.adobe.com/technologies/air/samples/</a><br /><br /></li><li>When you click on the download link you should be presented with the option to open the link with the Adobe Air Application Installer. Choose OK and follow the prompts. Make sure to select the option to install an icon on your desktop so that the app will be easy to find once installed.</li></ol>There you go. Go and explore the world of Adobe Air applications. Good times.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-48769467230719462942008-05-08T14:36:00.000-07:002008-05-09T08:39:35.266-07:00Setting up a Kiosk Watchdog for your Ubuntu Blackbox KioskPreviously I put together a post which describes how to build a <a href="http://kerkness.blogspot.com/2008/04/creating-touch-screen-kiosk-using-flex.html">Kiosk computer using Ubuntu, Blackbox and Firefox</a>. I'm following that up with details on how to monitor the kiosk so that you can be notified when/if the computer or services fail. I'll break this post into two sections.<br /><br />1) Monitoring the computer services and network<br />2) Monitoring FireFox performance<br /><br /><span style="font-size:180%;"><span style="font-weight: bold;">Monitoring the computer services and network</span></span><br /><br />Because our Kiosk computer is an Ubuntu server and running apache/php/mysql locally there are several open source network and service monitoring programs. The one I found most suitable for my solution is called <a href="http://www.tildeslash.com/monit/">Monit</a> and I found a very good post at <a href="http://www.ubuntugeek.com/monitoring-ubuntu-services-using-monit.html">Ubuntu Geek</a> describing how to install monit on ubuntu and configure it. I basically followed the Ubuntu Geek tutorial but made some modifications to the config file. My revised process is below.<br /><ol><li>Install Monit<br /><pre>sudo apt-get install monit</pre></li><li>Configure Monit. Open <span style="font-style: italic; font-weight: bold;">/etc/monit/monitrc</span> in your favorite text editor. Below is an example of how I set up my own configuration file. It should be pretty self explanatory.<br /><pre>## Start monit in background (run as daemon) and check the services at 2-minute<br />## intervals.<br />set daemon 120<br /><br />## Set syslog logging with the 'daemon' facility.<br />set logfile syslog facility log_daemon<br /><br />## Set list of mailservers for alert delivery.<br />## I use my ISP's SMTP server for better reliability and means<br />## I don't need an smtp server running on my Kiosk<br />set mailserver mail.shawcable.com<br /><br />## Use event queue if mailserver unavailable<br />set eventqueue<br />basedir /var/monit # set the base directory where events will be stored<br />slots 100 # optionaly limit the queue size<br /><br />## You can set the alert recipient here<br />set alert someone@domain.com<br /><br /># Monitor Apache<br />check process apache2 with pidfile /var/run/apache2.pid<br /><br /># Action to be taken when apache fails<br />start program = "/etc/init.d/apache2 start"<br />stop program = "/etc/init.d/apache2 start"<br /># Admin will notify by mail if below the condition satisfied below<br />if cpu is greater than 60% for 2 cycles then alert<br />if cpu > 60% for 5 cycles then restart<br />if children > 10 then alert<br />if children > 50 then restart<br />if loadavg(5min) greater than 10 for 8 cycles then stop<br />if 3 restarts within 5 cycles then timeout<br />group servers<br /><br /># Monitor MySQL<br />check process mysql with pidfile /var/run/mysqld/mysqld.pid<br />group database<br />start program = "/etc/init.d/mysql start"<br />stop program = "/etc/init.d/mysql stop"<br />if failed host 127.0.0.1 port 3306 then restart<br />if failed host 127.0.0.1 port 3306 then alert<br />if 5 restarts within 5 cycles then timeout<br /><br /># Monitor SSH Service<br />check process sshd with pidfile /var/run/sshd.pid<br />start program = "/etc/init.d/ssh start"<br />stop program = "/etc/init.d/ssh stop"<br />if failed port 22 protocol ssh then restart<br />if failed port 22 protocol ssh then alert<br />if 5 restarts within 5 cycles then timeout<br />group programs<br /><br /># Check services<br />check system localhost<br />if loadavg (1min) > 4 then alert<br />if loadavg (5min) > 2 then alert<br />if memory usage > 75% then alert<br />if cpu usage (user) > 70% then alert<br />if cpu usage (system) > 80% then alert<br />if cpu usage (wait) > 20% then alert</pre></li><li>Set Monit to start automagically. Open the file <span style="font-style: italic; font-weight: bold;">/etc/default/monit</span> and change the <span style="font-style: italic; font-weight: bold;">startup</span> value to <span style="font-style: italic; font-weight: bold;">1</span>. You can now start monit with the following command.<br /><pre>sudo /etc/init.d/monit start</pre></li></ol>If you want to be able to access Monit's web based interface remotely then check out the Ubuntu Geek tutorial for more information. I am not enabling this ability in my kiosk at present.<br /><span style="font-size:180%;"><br /><span style="font-weight: bold;">Monitoring FireFox performance</span></span><br /><br />By using Monit we are able to get good alerts regarding the overall health of our Kiosk. But Monit doesn't tell us is how our Kiosk client (Firefox) is behaving. If Firefox starts to eat up a percentage of your kiosk's available memory or CPU power you should be notified early.<br /><br />I myself am not that great at building BASH scripts so I opted to create a PHP script which tests a few conditions and sends an email if Firefox isn't running or is using too many resources. This script can be run as a cron job every few minutes. My PHP script requires the open source class <a href="http://sourceforge.net/projects/phpmailer">PHPMailer</a> which makes sending email from PHP a snap.<br /><br />Here is my php script.<br /><pre> /**<br />* This script gets the CPU and MEM usage of Firefox<br />*/<br />$cpu = 0;<br />$mem = 0;<br />$failed = false;<br /><br />// Get the PID of firefox<br />$pid = exec('pidof firefox-bin');<br /><br />// If firefox is running get memory<br />if ( $pid )<br />{<br />$status_str = exec('ps aux | grep "'. $pid .'" | grep -v grep');<br />$status = explode( ' ', $status_str );<br />// Strip blanks<br />foreach( $status as $k=>$v )<br />{<br /> if ( trim($v) == '' ) unset($status[$k]);<br />}<br />$status = array_values($status);<br /><br />$cpu = $status[2];<br />$mem = $status[3];<br /><br />if ( $cpu >= 60 || $mem >= 60 )<br />{<br /> $failed = true;<br /> $message = "Firefox is using $cpu% of CPU and $mem% of MEM";<br />}<br /><br />} else {<br /><br />// Firefox is not running<br />$failed = true;<br />$message = "Firefox is NOT running";<br /><br />}<br /><br />if ( $failed )<br />{<br />$body = date('l jS \of F Y h:i:s A',time() )."\n\n";<br />$body .= $message;<br /><br />// Create and Send Email<br />require_once( "/class/phpmailer/class.phpmailer.php");<br /><br />$mail = new PHPMailer();<br />$mail->From = ' firefox@mykiosk ';<br />$mail->FromName = " Firefox Status ";<br />$mail->Host = 'mail.shawcable.com';<br />$mail->Mailer = "smtp";<br /><br />$mail->Subject = "Firefox Issue on ArtTouch";<br />$mail->AddAddress(" someone@domain.com ");<br /><br />$mail->IsHtml(0);<br />$mail->Body = $body;<br /><br />// LOG RESULTS<br />if(! $mail->Send() ) {<br /> error_log("There was an error ending Firefox Performance Alert " . $mail->ErrorInfo );<br />}<br />}<br /></pre>If anyone wants to turn this into a bash script instead of PHP and share it that would be great.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6449760275592537835.post-17250165017670336842008-05-07T10:49:00.000-07:002008-05-08T07:48:40.324-07:00Accessing Ubuntu Gnome Applications from Mac OSX without using a Remote Desktop99% of the work I do is done on an Ubuntu server but I'm not always using an Ubuntu desktop. Recently I just bought myself a nice 24" iMac to use as my main workstation. Most of the time when I need to do something on the Ubuntu server I just open a terminal use ssh. However sometimes I find this approach a little cumbersome. When making large edits to config files on the server I find using a terminal based text editor to be a pain in the ass. Lucky for me there are a few ways to remotely access an Ubuntu desktop.<br /><br />The most common approach is to use a VNC client. For Mac OSX there is a client called Chicken which seems to be the most popular. This approach is easy to set up but for me I found it to be dreadfully slow and requires that the user is already logged onto the server.<br /><br /><span style="font-size:130%;"><span style="font-weight: bold;">Using X11 to access Gnome Applications on a Remote Ubuntu Server</span><br /></span><br />The process which I've found to suit my needs to a tea, is using the X11 utility which comes with Leopard and can be easily installed in Tiger or Panther. Here is an example of how to log into a remote Ubuntu server and use <span style="font-style: italic;">gEdit</span> to modify a text file. It works really fast and doesn't require a complete remote desktop type solution.<br /><ol><li>Start up an X11 terminal. You can find it at <span style="font-style: italic;">Applications > Utilities > X11</span><br /><br /></li><li>Use SSH to log into the remote Ubuntu machine using the <span style="font-style: italic;">-Y</span> option<br /><pre>ssh -Y user@192.168.1.3</pre></li><li>Use a terminal command to open a file in <span style="font-style: italic;">gEdit</span><br /><pre>sudo gedit /etc/php5/apache2/php.ini</pre></li><li>Make changes to the file and close <span style="font-style: italic;">gEdit</span> using the menu <span style="font-style: italic;">File > Quit</span></li></ol><span style="font-weight: bold;">UPDATE:<br /></span>I can confirm that at least with Leopard you don't need to start by opening an X11 terminal. You can log into the remote server using your default OSX <span style="font-style: italic;">Terminal</span> as long as you use the <span style="font-style: italic;">Y</span> option OSX will automatically start x11 when you try and launch a Gnome application.<span style="font-weight: bold;"><br /></span><br />Nice!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-50951800621801861582008-05-07T08:32:00.000-07:002008-05-07T08:46:25.498-07:00Integrating Grand Theft Auto IV with PHP and Flex ApplicationsIt can't be done. I've tried.<br /><br />I haven't posted any good tips or code snippets in the past week as I've been a little focused on opening up Manhattan in GTA. Managing priorities has never really been my problem.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-8186149765997773882008-04-25T13:07:00.000-07:002008-04-27T05:01:00.502-07:00Getting a performance boost from MySQLI have to admit I am no database guru but I do know how to organize my information into a decent set of tables. Every table has a <span style="font-style: italic;">primary key</span> and every field is just large enough and the right type for the data it needs to hold. In general my databases always perform well (enough) and I have little issue.<br /><br />Then while reading up on how to properly configure replication with MySQL I stumbled upon some information on <span style="font-style: italic;">Indexes</span> and I realized that I wasn't using my database as best I could.<br /><br />If you asked me a week ago what an <span style="font-style: italic;">'Index'</span> was in relation to a MySQL table I would have told you it was the <span style="font-style: italic;">'primary key'</span>. Which is only partially true. While the <span style="font-style: italic;">primary key</span> is automatically used as an <span style="font-style: italic;">Index</span> for a table it doesn't have to be the only <span style="font-style: italic;">Index</span> for the table.<br /><br /><span style="font-weight: bold;">What is an Index</span><br /><br />An <span style="font-style: italic;">Index</span> is basically a method MySQL (and other databases) use to organize a table to make it easier to search. For example, because the <span style="font-style: italic;">primary key</span> for any table is indexed (and unique) when you search the table for a record matching a <span style="font-style: italic;">primary key</span> value, the database knows it only needs to find 1 matching record. If there was no <span style="font-style: italic;">primary key</span> the database doesn't know how many possible matches it needs to find so it will have to look at every single record to try and find all possible matches.<br /><br />Knowing this, if you create <span style="font-style: italic;">Indexes</span> for your table on all fields which are frequently searched or are used in <span style="font-style: italic;">Join</span> statements you'll help your database find records more easily.<br /><br /><span style="font-weight: bold;">An Example</span><br /><br />I typically use a lot of joins when I query the database. Let's look at how one of these join queries perform without any <span style="font-style: italic;">Indexing</span> ( apart from the default indexing of my <span style="font-style: italic;">primary keys</span> ).<br /><br />In my database there is a table called 'artist' which contains the names, biography and details on different artists. The artist table has a <span style="font-style: italic;">primary key</span> field call artistid. In the same database there is another table called artwork which contains details on individual pieces of art. The artwork table has a <span style="font-style: italic;">primary key</span> field called artid, but it also contains its own artistid field. The artist and artwork tables have a one-to-many relationship, meaning that a single artist can have many works of art.<br /><br />Using this database design, you can load a complete listing of works of art by a single artist with the following query.<br /><pre>SELECT artwork.*, artist.* FROM artwork<br /> LEFT JOIN artist ON artwork.artistid=artist.artistid WHERE artwork.artistid=46;</pre>This query will work just fine. However if we want to gain some insight into how much information MySQL needs to process to perform this query we can add the EXPLAIN declaration to the front of the query.<br /><pre>EXPLAIN SELECT artwork.*, artist.* FROM artwork<br /> LEFT JOIN artist ON artwork.artistid=artist.artistid WHERE artwork.artistid=46;</pre>When we run this query at the command prompt MySQL gives the following response.<br /><pre>+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+<br />| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |<br />+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+<br />| 1 | SIMPLE | artwork | ALL | NULL | NULL | NULL | NULL | 3106 | Using where |<br />| 1 | SIMPLE | artist | const | artistid | artistid | 4 | const | 1 | |<br />+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+</pre>This tells us that when looking up information from the artist table the database was able to use the <span style="font-style: italic;">primary key</span> index and looked at a total of 1 rows in the table in order to find the information it needed. That seems pretty efficient. However when looking in the artwork table the database had do a full table scan and looked at 3106 rows to find matching records.<br /><br />Let's see what happens when we add an <span style="font-style: italic;">Index</span> on artistid to the artwork table.<br /><pre>ALTER TABLE artwork ADD INDEX( artistid );</pre>Now when we run the same EXPLAIN query we get the following details from MySQL<br /><pre>+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+<br />| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |<br />+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+<br />| 1 | SIMPLE | artwork | ref | artistid | artistid | 5 | const | 4 | Using where |<br />| 1 | SIMPLE | artist | const | artistid | artistid | 4 | const | 1 | |<br />+----+-------------+---------+-------+---------------+----------+---------+-------+------+-------------+</pre>Now doesn't that look much more efficient. This time because the database is keeping an <span style="font-style: italic;">Index</span> on the artistid field in both tables. It only needed to look at 4 rows from the artwork table to find the matching results.<br /><br />I did some additional performance testing to see how much of a boost adding this one extra <span style="font-style: italic;">Index</span> provided during peak times of database use. Prior to the indexing the query on average took 0.06 seconds, after the indexing it took 0.00. That's just on one tiny little query on a database that has only 3000 records. If you start adding extra indexes to all the tables in your database you should notice a nice boost in speed and performance.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-6449760275592537835.post-7216326020697008382008-04-19T07:59:00.001-07:002008-04-19T08:24:15.478-07:00Flex Tip: Publicly Accessible Private Variables in a Flex Class Or Another Reason to use Getters and SettersI've really fallen in love with using Getters and Setters for requesting and updating variable values in my flex classes. The most obvious reason for using Getters and Setters is having the ability to run some logic each time the value of a variable is updated or requested.<br /><br />Another elegant reason to use Getters and Setters which I've just stumbled upon is the ability to have a variable which is bindable and can be accessed publicly but can only be modified privately. I'll let you come up with your own reasons why this is useful. For me I wanted a custom component to be responsible for managing it's own state but needed the ability for other components to determine it's current state.<br /><br />Here's an example of how you might be used to working with variables. You have one variable which can be accessed or updated publicly and another which is just for internal use.<br /><pre>// a publicly accessible variable with binding<br />[Bindable] public var currentState:String;<br />// a private variable<br />private var internalState:String;<br /><br />// You could use a private function to update both variables.<br />private function updateCurrentState(value:String):void<br />{<br /> this.currentState = value;<br /> this.internalState = value;<br />}<br /></pre>The above code would work to meet my requirements but it's an obviously flawed and confusing approach. However without using Getters or Setters it would be the easiest way to meet the requirements.<br /><br />Here is how you can use Setters and Getters which let you use one variable that can be accessed publicly but only updated internally. Just use a public scope declaration on the <span style="font-style: italic;">get</span> function and a private scope declaration on the <span style="font-style: italic;">set</span> function. Easy and elegant.<br /><pre>private var _currentState:String;<br /><br />[Bindable] public function get currentState():String<br />{<br /> return this._currentState;<br />}<br />private function set currentState(value:String):void<br />{<br /> this._currentState = value;<br />}</pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6449760275592537835.post-31439336923680395342008-04-17T12:40:00.000-07:002008-04-17T12:55:19.439-07:00PHP explode() function in Actionscript for FlexIf you're a long time PHP programmer who has made the leap to building Flex based RIA applications in Flex you are probably asking yourself from time to time "how do I do a xxxxx function in actionscript". Well if you were recently wondering this about the PHP explode() function, here's your answer.<br /><pre>public function explode( delimiter:String, str:String ):Array<br />{<br /> return str.split( delimiter );<br />}</pre>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6449760275592537835.post-88906453199600770562008-04-17T11:15:00.000-07:002008-04-19T06:38:24.790-07:00warning: unable to bind to property 'XX' on class 'Object' (class is not an IEventDispatcher)<span style="font-weight: bold; font-style: italic;"></span>When working with data pulled in from an HTTPService in Flex you may find that at run time your application spits out a bunch of warnings that it is unable to bind to a property.<br /><br />If you want to prevent these warnings from occuring you should process your the data for your in actionscript instead of MXML.<br /><br />Change this ...<br /><pre><mx:Label text="{myresults.dynamiclabel}"/></pre>To this ... ( calling the function init() with the creationComplete event )<br /><pre><mx:Script><br />[Bindable] private var dynamiclabel:String;<br />private function init():void<br />{<br /> this.dynamiclabel = myresults.dynamiclabel;<br />}<br /></mx:Script><br /><mx:Label text="{this.dynamiclabel}"/></pre>I know this approach requires a little bit more code and the first approach works, but this way it doesn't result in any warnings. I'll make the assumption that no run-time warnings means more compliant code.<br /><br /><span style="font-weight: bold;">Another approach to solving the problem</span> is by wrapping Arrays in ArrayCollection and Objects in ObjectProxy wrappers. I got the following example <a href="http://livedocs.adobe.com/flex/2/langref/flash/events/IEventDispatcher.html">from here</a>.<br /><pre>function resultHandler(result:Array)<br />{<br />for(var i:String in result)<br /> {result[i] = new ObjectProxy(result[i]);}<br />targetArrayCollection = new ArrayCollection(result);<br />}</pre><span style="font-weight: bold;">What about using ItemRenderer(s)?<br /><br /></span>If you are combining the use of an <span style="font-style: italic;">ItemRenderer</span> and data from an <span style="font-style: italic;">HTTPService</span> in your application you may find that neither of the above solutions fully work. What you really need is a modification of the first solution. Collecting your data in response to the <span style="font-style: italic;">creationComplete</span> event will only collect data when the <span style="font-style: italic;">itemRenderer</span> is first created. The event is not called when dynamically change the <span style="font-style: italic;">dataProvider.</span> An approach to solving this scenario is the create a <span style="font-style: italic;">ChangeWatcher</span> in the <span style="font-style: italic;">ItemRenderer</span> to respond to changes to the <span style="font-style: italic;">data</span> property of said <span style="font-style: italic;">ItemRenderer.</span><span style="font-weight: bold;"><br /></span><br /><pre><mx:Script><br />import mx.binding.utils.ChangeWatcher;<br />import mx.events.PropertyChangeEvent;<br /><br />private var dataWatcher:ChangeWatcher;<br /><br />[Bindable] private var dynamiclabel:String;<br /><br />// ** Call this function from creationComplete event **<br />private function init():void<br />{<br /> this.dataWatcher = ChangeWatcher.watch( this, 'data', dataChangeHandler );<br /> this.dataChangeHandler(); <br />}<br /><br />private function dataChangeHandler(event:Event=null):void<br />{<br /> this.dynamiclabel = data.dynamiclabel;<br />}<br /></mx:Script><br /><mx:Label text="{this.dynamiclabel}"/><br /></pre>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-6449760275592537835.post-53220897900805054872008-04-16T12:12:00.001-07:002008-04-16T12:42:04.187-07:00Upgrading to FlexBuilder Linux Alpha 3 on Ubuntu 7.10 after Flexbuilder Alpha 2 expiresThis post is probably a month overdue but I've been working on a Mac OSX workstation for the last month ( my Dad has a nice new Mac so while he was on vacation I used his computer ). Anyway, my dad came back so I moved back to my trusty Ubuntu workstation at my own desk. To my surprise the Alpha version of the Adobe Flex Builder Linux had expired. I have licensed copies for Flex Builder for both Windows and Mac but there is no release for Linux yet so I'm left using alpha versions.<br /><br />The good news is that if your copy of Flex Builder Alpha 2 has expired (which I think everyone's did on March 15th) all you need to do is uninstall Flex Builder Alpha 2 and install Flex Builder Alpha 3. There are some new requirements however which makes this process a little more complicated if you're running Ubuntu. Mainly, Flex Builder Alpha 3 requires Eclipse 3.3 and the version of Eclipse which can be installed from Ubuntu repository is only version 3.2<br /><br />So here are the steps you need to follow if you want to get Flex Builder Alpha 3 installed on Ubuntu after Flex Builder Alpha 2 has expired. Instead of writing a full howto I'm going to link you to the different blogs/pages which have the instructions you'll need.<br /><ol><li>(Optional) If you haven't already upgraded to Ubuntu 7.10 now would be a good time. I was running Fiesty Fawn (which is 7.04 I think). <a href="http://www.ubuntu.com/getubuntu/upgrading">View this link to get that done.<br /><br /></a></li><li>Next you need to install Eclipse 3.3 on your system. <a href="http://portal.chrost.com/index.php?view=article&id=5%3Arunning-eclipse-33-on-ubuntu-710-gutsy-gibbon&option=com_content&Itemid=17">Follow the instructions at this link</a> but be insure to install 3.3 to a different location than 3.2. After you have 3.3 running you can copy the contents to the eclipse folder to the location where you have 3.2 installed. Mine was located at ~/.eclipse<br /><br /></li><li>Before installing Flex Builder Alpha 3 you'll need to uninstall Flex Builder Alpha 2. To do this you need to run the script Uninstall_Adobe_Flex_Builder_Linux. This will be located in the directory Uninstall Adobe_Flex_Builder_Linux which in turn exists in the Flex Builder Linux installation directory. <a href="http://labs.adobe.com/technologies/flex/flexbuilder_linux/releasenotes.html#install"> For full details see the Uninstall section of the release notes for alpha 3</a>.<br /><a href="http://labs.adobe.com/downloads/flexbuilder_linux.html"><br /></a></li><li><a href="http://labs.adobe.com/downloads/flexbuilder_linux.html">Download Flex Builder Alpha 3<br /><br /></a></li><li>Open your terminal and run the downloaded .bin file. For me I typed the following.<br /><pre>cd /home/ryan/Desktop<br />sh flexbuilder_linux_install_a3_033108.bin</pre>Follow the onscreen instructions.<br /><br /></li><li>When finished you should be able to fire up Alpha 3. You may find that you get an error when opening older project files saying that the file is out of sync. Just right-click on the project and select the 'Refresh' option.<br /><br /></li><li>If you need to add a launcher for the Flex Builder Alpha 3 program, the default location of the command to launch the program is<br /><pre>/home/ryan/Adobe_Flex_Builder_Linux/Adobe_Flex_builder.sh</pre>Assuming of course that you're user name is 'ryan' like mine is.<br /></li></ol>Unknownnoreply@blogger.com0