<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8526508655697471701</id><updated>2012-01-16T13:22:09.204-05:00</updated><category term='Network'/><category term='Printing'/><category term='MySQL'/><category term='PDF'/><category term='Javascript'/><category term='MediaWiki'/><category term='OTRS'/><category term='VirtualBox'/><category term='Review'/><category term='OpenSSL'/><category term='Gmail'/><category term='Photography'/><category term='FreeBSD'/><category term='Perl'/><category term='Extensions'/><category term='Apple'/><category term='Citrix'/><category term='OSX'/><category term='Board Games'/><category term='Google'/><category term='Programming'/><category term='Photoshop'/><category term='Fixes'/><category term='C#'/><category term='Boot Camp'/><category term='Automator'/><category term='PHP'/><category term='Regex'/><category term='Scripting'/><category term='Themes'/><category term='Firefox'/><category term='Smarty'/><category term='VMware'/><category term='Ubiquity'/><category term='Framework'/><category term='Astaro'/><category term='Linux'/><category term='Digital signature'/><category term='OSX Dock'/><category term='Flex'/><category term='iPad'/><category term='.NET'/><title type='text'>Jeremy Pyne</title><subtitle type='html'>This blog is a collection of my various projects and computer related endeavors.  Most of the posts deal with very specific issues/problems and solutions along with custom scripts, extensions, and various other subjects.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>60</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-8405935690684463423</id><published>2011-12-12T17:09:00.000-05:00</published><updated>2011-12-12T17:09:35.409-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='OTRS'/><title type='text'>Connecting OTRS into Active Directory.</title><content type='html'>I was testing out OTRS for work and got it to connect to and integrate with out Active Directory server.&amp;nbsp; All the Customers and Agents are pulled in from AD, bassed on group membership, along with their contact information.&amp;nbsp; This took quite a bit of trial and error but here is the config file I ended up with.&amp;nbsp; Note that this isn't the full file but rather goes into the load method where indicated.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;Kernel/Config.pm&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    # ---------------------------------------------------- #&lt;br /&gt;    # LDAP Integration for MyDomain.local                   #&lt;br /&gt;    # ---------------------------------------------------- #&lt;br /&gt;&lt;br /&gt;    # Enable LDAP lookups for Agent logins. User must be a member of OTRS Agents group.&lt;br /&gt;    $Self-&amp;gt;{'AuthModule'} = 'Kernel::System::Auth::LDAP';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::Host'} = 'dc01.mydomain.local';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::BaseDN'} = 'dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::UID'} = 'sAMAccountName';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::SearchUserDN'} = 'cn=otrs,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::SearchUserPw'} = 'otrs';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::AlwaysFilter'} = '(objectclass=user)';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::GroupDN'} = 'cn=OTRS Agents,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::AccessAttr'} = 'memberUid';&lt;br /&gt;    $Self-&amp;gt;{'AuthModule::LDAP::UserAttr'} = 'UID';&lt;br /&gt;&lt;br /&gt;    # Enable LDAP lookups of Agent account informations and default roles.&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule'} = 'Kernel::System::Auth::Sync::LDAP';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::Host'} = 'dc01.mydomain.local';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::BaseDN'} = 'dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::UID'} = 'sAMAccountName';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::SearchUserDN'} = 'cn=otrs,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::SearchUserPw'} = 'otrs';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::AlwaysFilter'} = '(objectclass=user)';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::GroupDN'} = 'cn=OTRS Agents,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::AccessAttr'} = 'memberUid';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::UserAttr'} = 'UID';&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::UserSyncMap'} = {&lt;br /&gt;        UserFirstname =&amp;gt; 'givenName',&lt;br /&gt;        UserLastname  =&amp;gt; 'sn',&lt;br /&gt;        UserEmail     =&amp;gt; 'mail',&lt;br /&gt;    };&lt;br /&gt;    $Self-&amp;gt;{'AuthSyncModule::LDAP::UserSyncRolesDefinition'} = {&lt;br /&gt;        'cn=OTRS Agents,cn=Users,dc=mydomain,dc=local' =&amp;gt; {&lt;br /&gt;            'Company Agents' =&amp;gt; 1,&lt;br /&gt;        },&lt;br /&gt;        'cn=Domain Admins,cn=Users,dc=mydomain,dc=local' =&amp;gt; {&lt;br /&gt;            'Tech Support Agents' =&amp;gt; 1,&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    # Enable LDAP lookups for Customer logins.&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::LDAP';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::Host'} = 'dc01.mydomain.local';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::BaseDN'} = 'dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::UID'} = 'sAMAccountName';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::SearchUserDN'} = 'cn=otrs,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::SearchUserPw'} = 'otrs';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::AlwaysFilter'} = '(objectclass=user)';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::GroupDN'} = 'ou=Companies,dc=mydomain,dc=local';&lt;br /&gt;#    $Self-&amp;gt;{'Customer::AuthModule::LDAP::GroupDN'} = 'cn=OTRS Customers,cn=Users,dc=mydomain,dc=local';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::AccessAttr'} = 'memberUid';&lt;br /&gt;    $Self-&amp;gt;{'Customer::AuthModule::LDAP::UserAttr'} = 'UID';&lt;br /&gt;&lt;br /&gt;    # Enable LDAP lookups for Customer account information.&lt;br /&gt;    $Self-&amp;gt;{CustomerUser} = {&lt;br /&gt;      Module =&amp;gt; 'Kernel::System::CustomerUser::LDAP',&lt;br /&gt;      Params =&amp;gt; {&lt;br /&gt;        Host =&amp;gt; 'dc01.mydomain.local',&lt;br /&gt;        BaseDN =&amp;gt; 'dc=mydomain,dc=local',&lt;br /&gt;        SSCOPE =&amp;gt; 'sub',&lt;br /&gt;        UserDN =&amp;gt; 'cn=otrs,cn=Users,dc=mydomain,dc=local',&lt;br /&gt;        UserPw =&amp;gt; 'otrs',&lt;br /&gt;        AlwaysFilter =&amp;gt; '(objectclass=user)',&lt;br /&gt;        GroupDN =&amp;gt; 'ou=Companies,dc=mydomain,dc=local',&lt;br /&gt;#        GroupDN =&amp;gt; 'cn=OTRS Customers,cn=Users,dc=mydomain,dc=local',&lt;br /&gt;        AccessAttr =&amp;gt; 'memberUid',&lt;br /&gt;        UserAttr =&amp;gt; 'UID',&lt;br /&gt;      },&lt;br /&gt;      CustomerKey =&amp;gt; 'sAMAccountName',&lt;br /&gt;      CustomerID =&amp;gt; '[customer_id]',&lt;br /&gt;      CustomerUserListFields =&amp;gt; ['sAMAccountName', 'sn', 'givenname', 'company',  'mail'],&lt;br /&gt;      CustomerUserSearchFields =&amp;gt; ['sAMAccountName', 'sn', 'givenname', 'company', 'mail'],&lt;br /&gt;      CustomerUserPostMasterSearchFields =&amp;gt; ['mail'],&lt;br /&gt;      CustomerUserNameFields =&amp;gt; ['givenname', 'sn'],&lt;br /&gt;      CustomerUserValidFilter =&amp;gt; '(company=*)',&lt;br /&gt;      Map =&amp;gt; [&lt;br /&gt;        [ 'UserSalutation', 'Title', 'title', 1, 0, 'var' ],&lt;br /&gt;        [ 'UserFirstname', 'Firstname', 'givenname', 1, 1, 'var' ],&lt;br /&gt;        [ 'UserLastname', 'Lastname', 'sn', 1, 1, 'var' ],&lt;br /&gt;        [ 'UserLogin', 'Login', 'sAMAccountName', 1, 1, 'var' ],&lt;br /&gt;        [ 'UserEmail', 'Email', 'mail', 1, 1, 'var' ],&lt;br /&gt;        [ 'UserCustomerID', 'CustomerID', 'company', 0, 1, 'var' ],&lt;br /&gt;        [ 'UserPhone', 'Phone', 'telephonenumber', 1, 0, 'var' ],&lt;br /&gt;        [ 'UserAddress', 'Address', 'postaladdress', 1, 0, 'var' ],&lt;br /&gt;        [ 'UserComment', 'Comment', 'description', 1, 0, 'var' ],&lt;br /&gt;      ],&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;You will need to change all references here to &lt;b&gt;MyDomain &lt;/b&gt;to your actual domain and the references to &lt;b&gt;dc01&lt;/b&gt; to your domain controller.&lt;br /&gt;&lt;br /&gt;Next add a user account names &lt;b&gt;otrs &lt;/b&gt;to the root Users folder in AD.&amp;nbsp; Set the password to &lt;b&gt;otrs &lt;/b&gt;and make them a domain user.&amp;nbsp; This account is simply used for otrs to talk to LDAP.&lt;br /&gt;&lt;br /&gt;Now add a group named &lt;b&gt;OTRS Agents&lt;/b&gt; to the root Users folder in AD.&amp;nbsp; Make any agents members of this group and also add the otrs account as a member.&lt;br /&gt;&lt;br /&gt;The Customers in the example above will include any user accounts in the Companies OU that I created in my AD server.&amp;nbsp; You can change the &lt;b&gt;ou=Companies,dc=mydomain,dc=local&lt;/b&gt; to reference a different OU or even a group membership, &lt;b&gt;OTRS Customers&lt;/b&gt; for example.&lt;br /&gt;&lt;br /&gt;In addition the &lt;b&gt;UserSyncRolesDefinitionvariable &lt;/b&gt;can be used to map agents to OTRS groups based on AD membership.&amp;nbsp; In the example above all members of Domain Admins are granted the Tech Support Agents role and members of the OTRS Agents are granted Company Agents.&amp;nbsp; These are not default groups but created in the OTRS admin screen and can have different permissions and ques.&amp;nbsp; Domain Admins for example can get to admin areas of OTRS while OTRS Agents can not.&lt;br /&gt;&lt;br /&gt;Another think you make want to consider is customizing the Customers screens to disable add/edit/delete operations as they won't do anything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-8405935690684463423?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/8405935690684463423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=8405935690684463423&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8405935690684463423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8405935690684463423'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/12/connecting-otrs-into-active-directory.html' title='Connecting OTRS into Active Directory.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3934211904648970156</id><published>2011-12-12T16:30:00.001-05:00</published><updated>2011-12-12T16:30:15.846-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Citrix'/><category scheme='http://www.blogger.com/atom/ns#' term='Printing'/><title type='text'>How to retain a users default printer in Citrix when using Session Printers.</title><content type='html'>I just covered how to push down Session Printers in Citrix based on Active Directory membership in my last post.&amp;nbsp; This method does however have one problem and that is that the users Default Printer preference will be discarded upon logout.&amp;nbsp; To get around that the free Defset utility can be set up in the users login script to restore the previous default printer.&amp;nbsp; It will also need to be added as a published application for the users to use to change their default printer instead of the normal way.&lt;br /&gt;&lt;br /&gt;You will need to download and save theDefSet utility onto each citrix server available form &lt;a href="http://www.ctrl-alt-del.com.au/CAD_Utils.htm"&gt;http://www.ctrl-alt-del.com.au/CAD_Utils.htm&lt;/a&gt;.&amp;nbsp; In my case I just saved it to %windir%.&lt;br /&gt;&lt;br /&gt;After that you will need to create a Group Policy that will apply to all the Citrix users.&amp;nbsp; In this policy you will need to add the following batch login script.&amp;nbsp; The delay is there to ensure all the printers have been generated before the utility is run.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;defset.bat&lt;/b&gt;&lt;br /&gt;&lt;code&gt;timeout 20&lt;br /&gt;start %windir%\defset.exe /min&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This will in essence work now but any client side printers published to the session will break it.&amp;nbsp; To deal with that we need to make two changes.&amp;nbsp; First off add the following Citrix Policy to the same Group Policy we already created.&lt;br /&gt;&lt;code&gt;Policy: Client printer names&lt;br /&gt;Value: Legacy printer names&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;br /&gt;Now save the following&amp;nbsp; VBScript onto your Citrix servers where you put DefSet. Any local printer on the server can be used here and it is only needed to get a fixed default printer prior to DefSet running so there there are no errors.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SetDefault.vbs &lt;/b&gt;&lt;br /&gt;&lt;code&gt;Set WSHNetwork = CreateObject("WScript.Network")&lt;/code&gt;&lt;br /&gt;&lt;code&gt;WSHNetwork.SetDefaultPrinter "Adobe PDF"&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;br /&gt;Next edit the &lt;b&gt;defset.bat&lt;/b&gt; file and added the following line before the DefSet code.&lt;br /&gt;&lt;code&gt;cscript.exe %windir%\SetDefault.vbs&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;br /&gt;Once done when a user loges in first all the printers will be set up by the Citrix Policies and the local shared&amp;nbsp; printers will be configured as well.&amp;nbsp; As that is happening the VBScript will set a fixed printer on the server as the default.&amp;nbsp; After 20 seconds the DefSet tool will launch and restore the users default printer.&amp;nbsp; The DefSet tool stores the preference in the users home directory witch&amp;nbsp; should automatically be backed up and restored fir the Citrix Profile manager.&lt;br /&gt;&lt;br /&gt;As a final option you can remove the icon from the system tray and add DefSet as a published application.&amp;nbsp; We like a clean System tray so I also added the following lines to the end of &lt;b&gt;defset.bat&lt;/b&gt; to close the system tray icon after a few seconds.&lt;br /&gt;&lt;code&gt;timeout 3&lt;/code&gt;&lt;br /&gt;&lt;code&gt;taskkill /FI "IMAGENAME eq DEFSET.exe" /FI "USERNAME eq %username%"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Please note that changing the default printer must be done with the DefSet tool and changing the printer in Windows will still be ignored on logoff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3934211904648970156?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3934211904648970156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3934211904648970156&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3934211904648970156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3934211904648970156'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/12/how-to-retain-users-default-printer-in.html' title='How to retain a users default printer in Citrix when using Session Printers.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4299928364652430547</id><published>2011-12-12T15:59:00.001-05:00</published><updated>2011-12-12T15:59:26.675-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Citrix'/><category scheme='http://www.blogger.com/atom/ns#' term='Printing'/><title type='text'>Granting users Session Printers bassed on Active Directory Membership with Citrix.</title><content type='html'>I have been setting up the new Citrix environment where I work on one of the biggest problems we have run into is that of printing.&amp;nbsp; In the end I set the system up to use Citrix Group Policies to configure Session Printers at login time.&amp;nbsp; Furthermore I have it configured to discard the printers at log off.&amp;nbsp; The primary benefit to this approach is a one to one relationship between the printers a user has access to and the group membership in Active Directory.&amp;nbsp; There is one group for each printer in AD those groups are granted to user/department groups as needed.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The Active Directory groups are named to match the printer names.&lt;br /&gt;&lt;code&gt;Printer - PNT001, Printer - PNT002-Color, Etc.&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The Citrix Policies can be configured in the GPO or the Citrix Delivery Services Console as User Policies, one entry per printer. and look like so.&lt;br /&gt;&lt;code&gt;Name: Printer - PNT001&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Setting: Session Printers&amp;nbsp;&amp;nbsp; \\print01\PNT001&lt;/code&gt;&lt;br /&gt;&lt;code&gt;Filter:&amp;nbsp; Allow when user is in DOMIAN\Printer - PNT001&amp;nbsp;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When the user logs in the Citrix policy is applied.&amp;nbsp; It has one session printer line for each printer that is only applied if the user is a member of that printers security group.&amp;nbsp; As a result when adding or removing printers in AD any changes will be reflected when the users log in again.&amp;nbsp; This does not set or force down a default printer and any default set by the user will be lost on log out as the printers are discarded.&amp;nbsp; Please see the next post for dealing with allowing users to set and store their default printer preference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4299928364652430547?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4299928364652430547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4299928364652430547&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4299928364652430547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4299928364652430547'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/12/granting-users-session-printers-bassed.html' title='Granting users Session Printers bassed on Active Directory Membership with Citrix.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-5484587399356862787</id><published>2011-09-16T01:05:00.001-04:00</published><updated>2011-09-17T02:25:27.221-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VMware'/><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Boot Camp'/><title type='text'>Using a single Windows 7 install with Boot Camp and VMware Fusion</title><content type='html'>&lt;b&gt;09/17/2011- Update: &lt;/b&gt; Updated the power on-vm-custom script to add &lt;b&gt;/persistent:yes&lt;/b&gt;. &amp;nbsp;Without this extra parameter the drive mapping immediately disconnects when the login scripts finish which is not what we are looking for.&lt;br /&gt;&lt;br /&gt;I recently put a SSD drive into my iMac running OSX Lion.  When I did this I also decided to create a single Windows 7 install for use in Boot camp and VMware Fusion.  Below is a brief overview of the process and the decisions I made along the way.&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;I started out by using the Boot Camp Assistant to create a 20 GB(Probably should have been closer to 30GB) partition on the SSD drive(120 GB) I installed.  I will only be using this partition for the operating system and drivers and have a 700GB external Firewire drive for all other data.&lt;br /&gt;&lt;br /&gt;I installed Windows 7 64bit on this volume and loaded it with the proper&amp;nbsp;&lt;a href="http://support.apple.com/downloads/#Boot%20Camp%20Windows%207"&gt;Windows 7 Boot camp drivers&lt;/a&gt; and all the Windows Updates.&lt;br /&gt;&lt;br /&gt;Next I installed &lt;a href="http://www.vmware.com/mac"&gt;VMware Fusion 3&lt;/a&gt; created a new Virtual Machine with the Boot Camp partition. &amp;nbsp;This is only a pointer to the partition and not a copy and as such the VM itself is only a few megabytes. &amp;nbsp;After this was done I booted up the VM and configured the VMware tools.&lt;br /&gt;&lt;br /&gt;I now have a single operating system that can be used in the VM or as a native OS. &amp;nbsp;To simplify the switching between the two Operating Systems I also installed &lt;a href="http://kainjow.com/"&gt;BootCamp&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Next I configured Windows to automatically log in when booting to speed the startup/reboot times. &amp;nbsp;To do this open Window, press Command-R, and enter &lt;b&gt;control userpassword2&lt;/b&gt;. not uncheck the option to require users to login and enter the username and password you want to automatically log in with.&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;Setting up shared access to a NTFS partition from both Boot Camp and VMware Fusion.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Next up is tackling the problem of the second hard drive. &amp;nbsp;I could have just created one large partition and been done now but the SSD eliminates the option. &amp;nbsp;Also I can't directly attach the external firewire drive to Windows XP become there is another partition on is for use in OSX. &amp;nbsp;As a result I am limited to make a few modifications to make it usable. &amp;nbsp;First off I configured the drive with two large partitions with Disk Utility(The first is a HFS+ volume with my OSX home directory and documents). &amp;nbsp;Then I Booted into Boot Camp and formatted the second column as NTFS for use in windows. &amp;nbsp;This drive will now automatically connet when booting in Boot Camp as E:\.&lt;br /&gt;&lt;br /&gt;This was only half the battle though as previously mentioned when booting in VMware this column isn't accessible. &amp;nbsp;The solution to this is to enable Sharing in the Virtual Machine settings. &amp;nbsp;Now click Add, select the proper volume, and make sure it is set to Read &amp;amp; Write. &amp;nbsp;Note that you definitely want to leave Mirrored folders off here or you will have problems when booting back to Boot Camp. &amp;nbsp;You can also add other folders to share here like tour home folder or other external volumes.&lt;br /&gt;&lt;br /&gt;Now you may have noticed that this volume is Read Only. &amp;nbsp;This is a limitation of OSX but there is a &lt;a href="http://macntfs-3g.blogspot.com/2010/10/ntfs-3g-for-mac-os-x-2010102.html"&gt;NTFS-3G Driver&lt;/a&gt; that will enable write access to the drive. &amp;nbsp;When installing be sure to chose the Cache option as the performance is needed and on a power failure only the programs will be vulnerable, not the OS itself. &amp;nbsp;Once installed you don't need to reboot, just click the X at the last screen then un mount the column and user Disk Utility to re-mount it. &amp;nbsp;You can now write to the NTFS volume all be it as a slightly reduced speed.&lt;br /&gt;&lt;br /&gt;Upon booting VMware we not get a Z:\ shared folder with this external drive listed within it. &amp;nbsp;This will not work though as the applications will not be installed in the same location in both modes. &amp;nbsp;To fix this we need to create a login and logout scrip in VMware. &amp;nbsp;Open up&amp;nbsp;&lt;b&gt;C:\Program Files\VMware\VMware Tools&lt;/b&gt;&amp;nbsp; and make copies of the&amp;nbsp;&lt;b&gt;poweron-vm-default&lt;/b&gt; and&amp;nbsp;&lt;b&gt;poweroff-vm-default&lt;/b&gt;&amp;nbsp;scripts. &amp;nbsp;Now open up My Computer, Z:\, and the shared external volume. &amp;nbsp;Copy the full path from the address bar at the top of the window. &lt;br /&gt;&lt;br /&gt;Edit the poweron-vm-copy script and add the following line where DRIVE is the drive letter expected in windows and PATH is the path copied previously.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;net use DRIVE: "PATH" /persistent:yes&lt;/code&gt;&lt;/pre&gt;Edit the poweroff-vm-copy script and add the following line where DRIVE is the same drive letter again.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;net use DRIVE: /delete&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For example here are mine:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;net use e: "\\vmware-host\Shared Folders\Windows HD" /persistent:yes&lt;br /&gt;net use e: /delete&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Finally open up VMware tools and change the Shut Down Guest and Power On Guest scripts to point to the appropriate files we just created. &amp;nbsp;Now reboot and enjoy seamless reboots and convenience.&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Other notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;With this setup and the SSD I can switch back and forth between anteing and emulated environments in less them a minute. &amp;nbsp;&lt;/li&gt;&lt;li&gt;I also have both operating systems configured to load the majority of their applications and data onto the external drive and hold only the OS and working files on the SSD. &amp;nbsp;The only real exceptions to this are in OSX where most applications are acutely on the SSD as in both OS's the user profiles are still on the SSD. &amp;nbsp;I all detail my OSX setup in a future post.&lt;/li&gt;&lt;li&gt;You will notice that you can't use BootChamp to reboot when the VM is running. &amp;nbsp;You need to shut down Windows first before you can reboot to Boot Camp. &amp;nbsp;VMware does not acutely need to be exited and I tend to leave the application running and the VM set to auto start so that earn I boot back to OSX the VM automatically starts back up.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-5484587399356862787?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/5484587399356862787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=5484587399356862787&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5484587399356862787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5484587399356862787'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/09/using-single-windows-7-install-with.html' title='Using a single Windows 7 install with Boot Camp and VMware Fusion'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2704052426367221458</id><published>2011-07-28T14:53:00.001-04:00</published><updated>2011-08-09T16:41:30.700-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><title type='text'>Remotly Enable VNC/Remote Management on OSX by SSH</title><content type='html'>I had the VNC based remote access feature of OS X enabled on 10.7 but somehow it got switched off.&amp;nbsp; I fiddled around a bit and figured out how to enable remote access from a remote SSH shell.&amp;nbsp; Note that these instructions will lead to manual configuration and you will not be able to enable/disabled the services from the System Preferences anymore.&amp;nbsp; In my case I used this to get in when I needed to but latter switched it back off and used the System Preferences setting.&lt;br /&gt;&lt;br /&gt;To manual switch on remote access from the terminal:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;ssh username@mac_ip_address&lt;br /&gt;cd /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources&lt;br /&gt;&lt;br /&gt;sudo ./kickstart -activate -configure -access -on -privs -all -restart -agent&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;To switch this override back off and thus allow the System Preferences to manage screen sharing again run the following. (This will disconnect any active sessions and require physical access to re-enable it.)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo ./kickstart -activate -restart -agent &lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2704052426367221458?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2704052426367221458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2704052426367221458&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2704052426367221458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2704052426367221458'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/07/remotly-enable-vncremote-management-on.html' title='Remotly Enable VNC/Remote Management on OSX by SSH'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1258163399973531313</id><published>2011-05-09T16:22:00.000-04:00</published><updated>2011-05-09T16:22:02.130-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><title type='text'>OSX Slow Boot / Can't Change Boot Volume</title><content type='html'>I recently had a very odd problem with my iMac.  I have a after market SSD in it and have &lt;a href="http://www.apple.com/support/bootcamp/"&gt;Boot Camp&lt;/a&gt; set up with Windows 7.  It has been working fine for about a year now but all of the sudden my boot time tripped and I was no longer able to switch OS's with &lt;a href="http://www.kainjow.com/"&gt;BootChamp&lt;/a&gt; nor set a startup volume in System Preferences.&lt;br /&gt;&lt;br /&gt;After some investigation I found the following symptoms and error messages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There were some scree rendering issues / I could mouse the mouse but not click or type.&lt;/li&gt;&lt;li&gt;I was able to use Remote Desktop / VNC to connect and interact with the computer properly.&lt;/li&gt;&lt;li&gt;In Activity Monitor the kernel_task process was using 100% of the CPU on one of the cores.&lt;/li&gt;&lt;li&gt;I could boot to Windows at startup and had no problems, errors, or lag there.&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;Could not find IODeviceTree:/options&lt;br /&gt;EFI found at IODeviceTree:/efi Mount point for /Volumes/BOOTCAMP is /Volumes/BOOTCAMP&lt;br /&gt;flushing fs disk buffer returned 0x5&lt;br /&gt;IOHIDSystem: postEvent LLEventQueue overflow.&lt;br /&gt;Can't access "efi-boot-device" NVRAM variable&lt;br /&gt;no bootx creation request&lt;br /&gt;nvram: nvram is not supported on this system&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One of the messages when I was testing the Unix bless command indicated that the "efi-boot-device" NVRAM variable was not accessible.  I then tried to examine it with the Unix nvram command only to get the "nvram: nvram is not supported on this system" error. I found the NVRAM error very odd And was surprised the computer was even booting if the nvram was defective.  I then tried to reset the NVRAM as it was not operating properly. &lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;Solution:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;As it turned out the problem all along was that the NVRAM had corrupted and was non-functional.  I did a hard reset of the NVRAM and all the problems went away immediately.  To reset the NVRAM you just reboot and hold down Command, Option, P, and R as the computer boots, or see this &lt;a href="http://support.apple.com/kb/ht1379"&gt;Apple Document&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1258163399973531313?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1258163399973531313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1258163399973531313&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1258163399973531313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1258163399973531313'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/05/osx-slow-boot-cant-change-boot-volume.html' title='OSX Slow Boot / Can&apos;t Change Boot Volume'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2140789007949090820</id><published>2011-04-15T10:11:00.001-04:00</published><updated>2011-04-15T11:58:39.365-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><title type='text'>Reading .NET JSON Date's in jQuery</title><content type='html'>ASP.Net writes out date objects to a JSON stream in the format { "\/Date(x)\/" } but jQuery will not automatically convert this to a JavaScript date but rather keeps it as a string.  To fix that this simple converter script can be included in your global JavaScript header.&lt;br /&gt;&lt;br /&gt;Note that this does requite jQuery 1.5+.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$.ajaxSetup({&lt;br /&gt;    // Add a custom converter to convert ASP.NET JSON dates to JS Date object&lt;br /&gt;    // That is convert { "\/Date(...)\/" } to { new Date(...) }&lt;br /&gt;    converters: {&lt;br /&gt;        "text json": function( textValue ) {&lt;br /&gt;            return jQuery.parseJSON(textValue.replace(/"\\\/Date\((-?\d*)\)\\\/"/g, "new Date($1)"));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2140789007949090820?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2140789007949090820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2140789007949090820&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2140789007949090820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2140789007949090820'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/04/reading-net-json-dates-in-jquery.html' title='Reading .NET JSON Date&apos;s in jQuery'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4971508242515402893</id><published>2011-04-13T16:20:00.003-04:00</published><updated>2011-05-09T12:06:57.761-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><title type='text'>Adding Better Ajax/JSON methods to HighCharts</title><content type='html'>I recently started using &lt;a href="http://www.highcharts.com/"&gt;Highcharts&lt;/a&gt; as the charting options in Crystal Reports are rather limited.  Here is a small patch I made to simplify the loading of chart data from JSON server calls via AJAX.  This does not do anything incredibly unique, just simplifies the code to do so by adding an ajaxSource property to the chart and series configurations that is loaded on init and a reloadAjax method to automatically reload the data.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;05/03/2011 - Rewrote the code as a extension instead of a patch and added support for setting multiple series via options.chart.ajaxSource.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can set an ajaxSource for an individual series like in this example.&lt;br /&gt;&lt;pre&gt;chart = new Highcharts.Chart({&lt;br /&gt;  chart: {&lt;br /&gt;    renderTo: 'container',&lt;br /&gt;    type: 'spline'&lt;br /&gt;  },&lt;br /&gt;  xAxis: {&lt;br /&gt;    type: 'datetime' }&lt;br /&gt;  },&lt;br /&gt;  series: [{&lt;br /&gt;    name: 'Logins',&lt;br /&gt;    data: [],&lt;br /&gt;    ajaxSource: '/Home/GetLoginsOverTime'&lt;br /&gt;  }]&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In this case the server needs to return a JSON object with aaData in style of the &lt;a href="http://www.highcharts.com/ref/#series"&gt;options.series.data&lt;/a&gt; property.&lt;br /&gt;&lt;pre&gt;{"aaData":[&lt;br /&gt; {"x":1300406400000,"y":3},{"x":1300410000000,"y":4},{"x":1300413600000,"y":5}&lt;br /&gt;]}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Alternately you can set an ajaxSource for the entire chart that returns multiple series of data.  These two styles can also be combined.&lt;br /&gt;&lt;pre&gt;chart = new Highcharts.Chart({&lt;br /&gt;  chart: {&lt;br /&gt;    renderTo: 'container',&lt;br /&gt;    type: 'spline',&lt;br /&gt;    ajaxSource: '/Home/GetTableActivity'&lt;br /&gt;  },&lt;br /&gt;  xAxis: {&lt;br /&gt;    type: 'datetime' }&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In this case the returned JSON object should have an aaData in the style of an array of options.series objects.&lt;br /&gt;&lt;pre&gt;{"aaData":[&lt;br /&gt;  {"name":"users","data":[&lt;br /&gt;    {"x":1299888000000,"y":3},{"x":1299891600000,"y":13},{"x":1299895200000,"y":4}]},&lt;br /&gt;  {"name":"messages","data":[&lt;br /&gt;    {"x":1299888000000,"y":4},{"x":1299891600000,"y":33},{"x":1299895200000,"y":5}]}&lt;br /&gt;]}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can then call chart.reloadAjax(); to reload the chart data without reloading the page.  This will reload all ajaxSource's in the chart, it is not possible to only do one series at a time. This is more useful when combined with automatic parameter replacement take this next sample for example.&lt;br /&gt;&lt;pre&gt;chart = new Highcharts.Chart({&lt;br /&gt;  chart: {&lt;br /&gt;    renderTo: 'container',&lt;br /&gt;    type: 'spline',&lt;br /&gt;    ajaxSource: '/Home/GetTableActivityByDate?date=#date'&lt;br /&gt;  },&lt;br /&gt;  xAxis: {&lt;br /&gt;    type: 'datetime' }&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&amp;nbsp; &lt;br /&gt;Instead of setting a date in code or writing code to manage this we use #date.  This will cause the Chart.init() and Chart.reloadAjax() calls to look for an object with the ID date and use that instead when making the AJAX call. Multiple parameters can be combined and reloadAjax can be triggered whenever they change.  This input box can be added to the previous example.&lt;br /&gt;&lt;pre&gt;&amp;lt;input type='text' id='date' change='chart.reloadAjax()'/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Chart.reloadAjax(appendData, shift) method also has two optional parameters that can be passed in.  The first is appendData, if this is true the new points will be added to the series in question instead of replacing them.  The second is shift, if this is true then as each point is added the oldest point in that same series will be removed.  &lt;br /&gt;Note that appendData does not check for duplicates and will corrupt the chart is the same span is reloaded and that shift only works going forward in time not backwards.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here is a sample bit of server side code that can be loaded via a JSON request.  This example is using MVC and the JSON View but you can also return stright JSON from ASP.NET with System.Web.Script.Serialization.JavaScriptSerializer().Serialize() or JSONSharp.JSONReflector().&lt;br /&gt;&lt;pre&gt;public ActionResult GetActivity(DateTime date)&lt;br /&gt;{&lt;br /&gt;    // Get a day worth of data.&lt;br /&gt;    var all = activityLogRepository.FindAllActivityLogs(date);&lt;br /&gt;    // Group the data but hour.  I am using an enumerable and GroupBy but this isn't accurate as it does not include hours with no records.&lt;br /&gt;    // The correct way to do it would be a for(x = 1; x &amp;lt;= 24; x++) data.push({ x = x, y = all.Where(item =&amp;gt; item.timestamp.hour = x).count()};&lt;br /&gt;    var data = all.GroupBy(item =&amp;gt; item.timestamp.Hour, (key, list) =&amp;gt; new { x = key, y = list.Count() } );&lt;br /&gt;    // Return the data under aaData not as a top level item so we can pass errors back.&lt;br /&gt;    return Json(new { aaData = data });&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;highcharts.js&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;// Load data from the AjaxSource specified in the chard on load.  To do this add a callback to the chart init event.&lt;br /&gt;window.Highcharts.Chart.prototype.callbacks.push(function(chart) {&lt;br /&gt;    chart.reloadAjax(false);&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;// Reload all the data in the chart from the ajaxSource of the chart or individial series. Multiple ajaxSource items may be specified so &lt;br /&gt;//  we dont have the option of overriding the ajaxSource.  Instead use a #param variable in the ajaxSource and set the parameter via a input field. &lt;br /&gt;// If appendData is set then charts will not me empties but rather new data will be merged in.&lt;br /&gt;// If shift is set then set then individual points will be cycled out as new points are added to a series.  Note that this isn't chart wide so a &lt;br /&gt;//  series that isn't updated won't change at all, you would have to pass zero's in or manualy remove the old data to make the entire chart rotate as one.&lt;br /&gt;window.Highcharts.Chart.prototype.reloadAjax = function(appendData, shift) {&lt;br /&gt;    var chart = this;&lt;br /&gt;&lt;br /&gt;    // Overwrite existing data by default.&lt;br /&gt;    if (typeof appendData == 'undefined')&lt;br /&gt;        appendData = false;&lt;br /&gt;&lt;br /&gt;    chart.showLoading();&lt;br /&gt;&lt;br /&gt;    // Either append data to waht is already in the chart or reset the chart.&lt;br /&gt;    if (appendData) {&lt;br /&gt;        // Look for any series with an ajaxSource.&lt;br /&gt;        $($.grep(chart.series, function(chartData) { return chartData.options.ajaxSource != null; })).each(function() {&lt;br /&gt;            var chartSeries = this;&lt;br /&gt;&lt;br /&gt;            // Read the chart data from a JSON query.&lt;br /&gt;            $.getJSON(chart.updateProperties(chartSeries.options.ajaxSource), null, function(json) {&lt;br /&gt;                for (var point in json.aaData) {&lt;br /&gt;                    // Add each point to the series.&lt;br /&gt;                    chartSeries.addPoint(json.aaData[point], false, shift);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // Update the chart.  There may be more pending JSON requests but can't easily determine that so we show this early.&lt;br /&gt;                chart.redraw();&lt;br /&gt;                chart.hideLoading();&lt;br /&gt;            });&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        if (chart.options.chart.ajaxSource) {&lt;br /&gt;            // Read the chart data from a JSON query.&lt;br /&gt;            $.getJSON(chart.updateProperties(chart.options.chart.ajaxSource), null, function(json) {&lt;br /&gt;                // For each series in the results.&lt;br /&gt;                for (var jsonSeries in json.aaData) {&lt;br /&gt;                    // Look for an existing series in the chart with the same name.&lt;br /&gt;                    var chartSeries = $.grep(chart.series, function(chartData) { return chartData.name == json.aaData[jsonSeries].name })[0];&lt;br /&gt;&lt;br /&gt;                    if (chartSeries == null) {&lt;br /&gt;                        // If there isn't one then add a new series.&lt;br /&gt;                        chart.addSeries(json.aaData[jsonSeries], false);&lt;br /&gt;                    } else {&lt;br /&gt;                        // Otherwise add a new series.&lt;br /&gt;                        for (var point in json.aaData[jsonSeries].data) {&lt;br /&gt;                            // Add each point to the series.&lt;br /&gt;                            chartSeries.addPoint(json.aaData[jsonSeries].data[point], false, shift);&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // Update the chart.&lt;br /&gt;                chart.redraw();&lt;br /&gt;                chart.hideLoading();&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;    } else {&lt;br /&gt;        // Look for any series with an ajaxSource.&lt;br /&gt;        $($.grep(chart.series, function(chartData) { return chartData.options.ajaxSource != null; })).each(function() {&lt;br /&gt;            var chartSeries = this;&lt;br /&gt;&lt;br /&gt;            // Read the chart data from a JSON query.&lt;br /&gt;            $.getJSON(chart.updateProperties(chartSeries.options.ajaxSource), null, function(json) {&lt;br /&gt;                // Put the new data into the series.&lt;br /&gt;                chartSeries.setData(json.aaData);&lt;br /&gt;&lt;br /&gt;                // Update the chart.  There may be more pending JSON requests but can't easily determine that so we show this early.&lt;br /&gt;                chart.hideLoading();&lt;br /&gt;            });&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        if (chart.options.chart.ajaxSource) {&lt;br /&gt;            // Read the chart data from a JSON query.&lt;br /&gt;            $.getJSON(chart.updateProperties(chart.options.chart.ajaxSource), null, function(json) {&lt;br /&gt;                for (var chartSeries = 0; chartSeries &amp;lt; chart.series.length; ) {&lt;br /&gt;                    // Remove the old series unless they have their own ajaxSource.&lt;br /&gt;                    if (chart.series[chartSeries].options.ajaxSource == null)&lt;br /&gt;                        chart.series[chartSeries].remove(false);&lt;br /&gt;                    else&lt;br /&gt;                        chartSeries++;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                for (var jsonSeries in json.aaData) {&lt;br /&gt;                    // Put the new data into the series.&lt;br /&gt;                    chart.addSeries(json.aaData[jsonSeries], false);&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // Update the chart.&lt;br /&gt;                chart.redraw();&lt;br /&gt;                chart.hideLoading();&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Update an impending ajax calls request url to reflect local data.  For each #word formated&lt;br /&gt;//  block in the url we will look for a tag with the same id, minus the #, and update our url&lt;br /&gt;//  with the found value.  If no object with a matching id is found the original value is left.&lt;br /&gt;// Eg: Take the url /Load/#uid/#date.  This will cause the #uid to get replaced with $("#uid").val()&lt;br /&gt;//  and the #date to get replaced with $("#date").val().&lt;br /&gt;window.Highcharts.Chart.prototype.updateProperties = function(url) {&lt;br /&gt;    var parts = url.replace(/%23/g, "#").split(/(#\w+)/);&lt;br /&gt;    var result = "";&lt;br /&gt;    for (var count = 0; count &amp;lt; parts.length; count++) {&lt;br /&gt;        if (parts[count].match(/^#/)) {&lt;br /&gt;            if ($(parts[count]).is("input[type=checkbox]"))&lt;br /&gt;                result += $(parts[count]).is(':checked')&lt;br /&gt;            else if ($(parts[count]).is("span"))&lt;br /&gt;                result += $(parts[count]).text()&lt;br /&gt;            else&lt;br /&gt;                result += $(parts[count]).val()&lt;br /&gt;        } else&lt;br /&gt;            result += parts[count];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return result;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4971508242515402893?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4971508242515402893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4971508242515402893&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4971508242515402893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4971508242515402893'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2011/04/adding-better-ajaxjson-methods-to.html' title='Adding Better Ajax/JSON methods to HighCharts'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-27153920124300207</id><published>2010-08-12T16:31:00.000-04:00</published><updated>2010-08-12T16:31:54.825-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Mass rename files in Midnight Commander</title><content type='html'>Here is another script for Midnight Commander to allow for mass renaming of flagged files.  To use it you will need to add it to your MC menu file in /etc/mc/mc.menu or ~/.mc/menu.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;+ t t&lt;br /&gt;e       Rename tagged files&lt;br /&gt;        set %t; CMD=%{Enter regex}&lt;br /&gt;        while [ -n "$1" ]; do&lt;br /&gt;          echo rename -\"s"$CMD"e\" "$1"&lt;br /&gt;          rename -\"s"$CMD"e\" "$1"&lt;br /&gt;          shift&lt;br /&gt;        done&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;first you will need to flag the files toy want to rename then execute the user operation.  When you do so you will be prompted for the regular expression you with to execute.  &lt;br /&gt;&lt;br /&gt;Some examples:&lt;br /&gt;&lt;pre&gt; /oldword/newword/   # Change a word.&lt;br /&gt; /_/ /g   # Replace all _'s with spaces.&lt;br /&gt; /_\[.*\]//   # Remove _[CRCHASH] tags.&lt;br /&gt; /\(.*\)//   # Remove (...) sections.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-27153920124300207?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/27153920124300207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=27153920124300207&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/27153920124300207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/27153920124300207'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/08/mass-rename-files-in-midnight-commander.html' title='Mass rename files in Midnight Commander'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6272137466691147170</id><published>2010-08-12T16:26:00.001-04:00</published><updated>2011-05-09T16:19:39.352-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Comapring files with md5 Checksums in Midnight Commander</title><content type='html'>I wrote a few scripts for Midnight Commander to easily compare multiple copies of a directory via checksums.&amp;nbsp; There are a few variations listed below along with a bash script that does the same thing.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;First off is this simple bash script that can be used stand alone but is also required for the MC menu entries.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;/usr/local/bin/md5compare&lt;/b&gt;&lt;pre&gt;#!/usr/local/bin/bash&lt;br /&gt;echo -n "Comparing $1 to $2: "&lt;br /&gt;if [ ! -e "$1" ] || [ ! -e "$2" ]&lt;br /&gt;then&lt;br /&gt; echo File Missing&lt;br /&gt; exit 1; &lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;if [ $(md5 -q "$1") = $(md5 -q "$2") ]&lt;br /&gt;then&lt;br /&gt; echo Files Match&lt;br /&gt; exit 0;&lt;br /&gt;else&lt;br /&gt; echo Files Different&lt;br /&gt; exit 1;&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You may need to tweak the bash and script paths for your installation.  Also remember to make it executable.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo chmod +x /usr/local/bin/md5compare&lt;/pre&gt;&lt;br /&gt;To execute this command directly just run the following.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;md5compare file1 file2&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Next up are two comparison scripts that can be added MC's user menu.&amp;nbsp; To do this append them to the mc.menu file in your system /etc or users ~/.mc/menu.&lt;br /&gt;&lt;br /&gt;This script will compare the currently highlighted file with one of the same name in the other panel.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;+ t n&lt;br /&gt;w       Compare files via MD5 checksum&lt;br /&gt;        /usr/local/bin/md5compare %f %D/%f&lt;br /&gt;        echo Press any key to continue&lt;br /&gt;        read key&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This script on the other hand will compare all tagged files to any matching files in the other panel.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;+ t t&lt;br /&gt;W       Compare tagged files via MD5 checksum&lt;br /&gt;        for i in %t&lt;br /&gt;        do &lt;br /&gt;          /usr/local/bin/md5compare "$i" %D/"$i"&lt;br /&gt;        done&lt;br /&gt;        echo Press any key to continue&lt;br /&gt;        read key&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally here is a menu item to simply dispaly the checksum of the selected file.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;+ t n&lt;br /&gt;s       Calculate MD5 checksum&lt;br /&gt;        echo Calculating checksum of %f&lt;br /&gt;        md5 -q %f&lt;br /&gt;        echo Press any key to continue&lt;br /&gt;        read key&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6272137466691147170?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6272137466691147170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6272137466691147170&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6272137466691147170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6272137466691147170'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/08/comapring-files-with-md5-checksums-in.html' title='Comapring files with md5 Checksums in Midnight Commander'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2913664416394310628</id><published>2010-06-28T08:53:00.000-04:00</published><updated>2010-06-28T08:53:12.876-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='OTRS'/><title type='text'>Tracking Customer Companies in OTRS.</title><content type='html'>I have been playing abound with &lt;a href="http://www.otrs.com/en/"&gt;OTRS&lt;/a&gt; for the past few days and ran into a bit of a snag.&amp;nbsp; I installed it from the Ubuntu repositories but noticed that the Customers can't be linked to the Companies witch would be a useful feature.&amp;nbsp; I also found out after some digging that there was no option in the SysConfig to fix this.&amp;nbsp; I found the setting by manually editing the config files but this was of limited use.&amp;nbsp; In the end I had to make a few manual edits to make the system work as expected.&amp;nbsp; I outlined the changes below to the base 2.4.7 install.&lt;br /&gt;&lt;br /&gt;First open a terminal and cd to your otrs install directory.&lt;br /&gt;&lt;pre&gt;cd /usr/share/otrs&lt;/pre&gt;&lt;br /&gt;Enable CustomerCompanySupport&lt;br /&gt;&lt;pre&gt;sudo vim Kernel/Config.pm&lt;/pre&gt;Add the following line inside the sub Load method.&lt;br /&gt;&lt;pre&gt;# Enable Customer Company linking.&lt;br /&gt;$Self-&amp;gt;{CustomerUser}-&amp;gt;{CustomerCompanySupport} = 1;&lt;/pre&gt;&lt;br /&gt;Run the mysql client and add a field to the customer table.&lt;br /&gt;&lt;pre&gt;mysql -u root -p&lt;br /&gt;use otrs2;&lt;br /&gt;alter table customer_user add column company_id varchar(100);&lt;br /&gt;exit&lt;/pre&gt;&lt;br /&gt;Register the extra row with the editor.&lt;br /&gt;&lt;pre&gt;sudo vim Kernel/Config/Defaults.pm&lt;/pre&gt;Find the CustomerUser variable and then the Map array within it.&amp;nbsp; Add the following line after the UserCustomerID field.&lt;br /&gt;&lt;pre&gt;[ 'UserCompanyID',&amp;nbsp; 'CompanyID',&amp;nbsp; 'company_id',&amp;nbsp; 0, 1, 'var', '', 0 ],&lt;/pre&gt;&lt;br /&gt;Change the company dropdown list to trigger on the new CompanyID field.&lt;br /&gt;&lt;pre&gt;sudo vim Kernel/Modules/AdminCustomerUser.pm&lt;/pre&gt;Look for the reference to UserCustomerID and change it to UseCompanyID. It should look like this now.&lt;br /&gt;&lt;pre&gt;$Entry-&amp;gt;[0] =~ /^UserCompanyID$/i&lt;/pre&gt;&lt;br /&gt;Change the dispaly logic to match against the new CompanyID.&lt;br /&gt;&lt;pre&gt;sudo vim Kernel/System/CustomerUser.pm&lt;/pre&gt;Look for the reference to UserCustomerID and change it to UseCompanyID.  It should look like this now. &lt;br /&gt;&lt;pre&gt;CustomerID =&amp;gt; $Customer{UserCompanyID},&lt;/pre&gt;&lt;br /&gt;You can now restart apache and the changes should show up.&lt;br /&gt;&lt;pre&gt;sudo /etc/init.d/apache2 restart&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2913664416394310628?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2913664416394310628/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2913664416394310628&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2913664416394310628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2913664416394310628'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/06/tracking-customer-companies-in-otrs.html' title='Tracking Customer Companies in OTRS.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3195539608674556298</id><published>2010-05-06T23:33:00.000-04:00</published><updated>2010-05-06T23:33:44.997-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><category scheme='http://www.blogger.com/atom/ns#' term='Photoshop'/><title type='text'>A good version...</title><content type='html'>I previously posted a comparison of the &lt;a href="http://pynej.blogspot.com/2010/03/doing-photoshop-cs5-content-aware-fill.html"&gt;Resynthesizer plugin for GIMP&lt;/a&gt; and &lt;a href="http://pynej.blogspot.com/2010/05/adobe-photoshop-cs5-content-aware-fill.html"&gt;Content Aware Fill feature of Adobe Photoshop CS5&lt;/a&gt;. &amp;nbsp;I created a good version of the big tree the lake but I wanted to post it separately as this is not part of the comparison. &amp;nbsp;This is version was made with multiple Content Aware Fill and Healing Brush passes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.flickr.com/photos/pynej/4585887238/" style="margin-left: 1em; margin-right: 1em;" title="IMG_1069-2 by pynej, on Flickr"&gt;&lt;img alt="IMG_1069-2" height="320" src="http://farm5.static.flickr.com/4010/4585887238_e354bbfd41_b.jpg" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3195539608674556298?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3195539608674556298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3195539608674556298&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3195539608674556298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3195539608674556298'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/05/good-version.html' title='A good version...'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm5.static.flickr.com/4010/4585887238_e354bbfd41_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6443245991501399623</id><published>2010-05-06T23:27:00.000-04:00</published><updated>2010-05-06T23:27:35.708-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><category scheme='http://www.blogger.com/atom/ns#' term='Photoshop'/><title type='text'>Adobe Photoshop CS5 Content Aware Fill</title><content type='html'>I posted an &lt;a href="http://pynej.blogspot.com/2010/03/doing-photoshop-cs5-content-aware-fill.html"&gt;article&lt;/a&gt;&amp;nbsp;previously about the &lt;a href="http://www.logarithmic.net/pfh/resynthesizer"&gt;Resynthesizer&lt;/a&gt;&amp;nbsp;plugin for GIMP. &amp;nbsp;I took the same pictures as before and did the same edits this time with the&amp;nbsp;CS5 Content Aware Fill and here are the results. &amp;nbsp;Again I will point out that these pictures are doing a "Single Pass" without doing any manual edits or touchups and are just a test of the Content-Aware logic.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 15px; white-space: pre-wrap;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_b.jpg" width="320" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/pynej/4585280903/" title="IMG_1065 by pynej, on Flickr"&gt;&lt;img alt="IMG_1065" height="240" src="http://farm5.static.flickr.com/4048/4585280903_a2731f1b46_b.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://farm4.static.flickr.com/3166/2923111412_aa50594c58_b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://farm4.static.flickr.com/3166/2923111412_aa50594c58_b.jpg" width="240" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/pynej/4585886876/" title="IMG_1069 by pynej, on Flickr"&gt;&lt;img alt="IMG_1069" height="320" src="http://farm5.static.flickr.com/4055/4585886876_b7876f4786_b.jpg" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6443245991501399623?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6443245991501399623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6443245991501399623&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6443245991501399623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6443245991501399623'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/05/adobe-photoshop-cs5-content-aware-fill.html' title='Adobe Photoshop CS5 Content Aware Fill'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4641823694345066260</id><published>2010-04-21T14:59:00.002-04:00</published><updated>2010-04-21T15:00:57.384-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Board Games'/><title type='text'>Extra BoardGameGeek Record a Play links.</title><content type='html'>This is a simple Greasemonkey script that adds "Record a Play" links to the played games lists.  This is useful when browsing your friends Recent Plays list as it allows you to directly add a play from the page without having to first open the game.  It also sets the play date to that of the original item so if you both played the same game its a simple two click to copy their play to your own account.&lt;br /&gt;&lt;br /&gt;A picture is worth a thousand words:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/_546vW2tHQGQ/S89I97NaXfI/AAAAAAAAAhw/irjlqMdIua8/s1600/Game+Plays.gif" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="360" src="http://3.bp.blogspot.com/_546vW2tHQGQ/S89I97NaXfI/AAAAAAAAAhw/irjlqMdIua8/s640/Game+Plays.gif" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Installation Instructions:&lt;/b&gt;&lt;br /&gt;Install the &lt;a href="http://www.greasespot.net/"&gt;Greasemonkey plug-in&lt;/a&gt; for your web browser.&lt;br /&gt;Restart your web browser as necessary.&lt;br /&gt;Install the &lt;a href="http://userscripts.org/scripts/show/44063"&gt;Greased MooTools&lt;/a&gt; and &lt;a href="http://userscripts.org/scripts/show/74943"&gt;Extra Log Plays Links&lt;/a&gt; scripts into Greasemonkey.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4641823694345066260?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4641823694345066260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4641823694345066260&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4641823694345066260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4641823694345066260'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/extra-boardgamegeek-record-play-links.html' title='Extra BoardGameGeek Record a Play links.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_546vW2tHQGQ/S89I97NaXfI/AAAAAAAAAhw/irjlqMdIua8/s72-c/Game+Plays.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6581295589235786608</id><published>2010-04-16T13:48:00.000-04:00</published><updated>2010-04-16T13:48:00.638-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Framework'/><title type='text'>PHP MySQL database abstraction class</title><content type='html'>I wrote a very elegant database interaction class for PHP some time ago.&amp;nbsp; The class is a simple layer between a PHP application and its database and provides a very clean and efficient interface to the database.&amp;nbsp; It does not generate the SQL code for you, but rather it makes a cleaner method of calling your SQL code.&amp;nbsp; It allows you to generate repeatable queries as objects, provides parameter substitution in queries, and allows reading a record via class accessors.&amp;nbsp; Some samples of these features are shown below.&lt;br /&gt;&lt;br /&gt;I have not posted the source code itself as I feel this is one of my more exquisite projects and I don't want to see it taken without credit.&amp;nbsp; I may be willing to provide the code on request though.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Examples: A simple reader query. (Select)&lt;br /&gt;&lt;pre&gt;$users = new query("SELECT userID, username, lastAccess, enabled FROM users;");&lt;br /&gt;&lt;br /&gt;if(!$users-&amp;gt;is_valid())&lt;br /&gt;   return false;&lt;br /&gt;&lt;br /&gt;while($users-&amp;gt;fetch())&lt;br /&gt;{&lt;br /&gt;    echo $users-&amp;gt;usersID;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A simple non-reader query. (Insert, Update, Delete)&lt;br /&gt;&lt;pre&gt;if(!query::run("UPDATE users SET session = '$userSessionID', lastAccess = NOW() WHERE userID = $userID;"))&lt;br /&gt;    throw new Exception("Database update failed.");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The same update only using parameters instead of string substitution. There are two ways to do this and both generate identical SQL code.&lt;br /&gt;&lt;pre&gt;return query::run("UPDATE users SET session = @1, lastAccess = NOW() WHERE userID = @2;", $userSessionID, $userID);&lt;br /&gt;&lt;br /&gt;return query::run("UPDATE users SET session = @sessionID, lastAccess = NOW() WHERE userID = @usersID;",&lt;br /&gt;   array(sessionID =&amp;gt; $userSessionID, usersID =&amp;gt; $userID));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All three of the above update calls will generate the following statement. Notice how the second two statements automatically quote string and escape any special characters in strings.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;UPDATE users SET session = 'SESSION', lastAccess = NOW() WHERE userID = UID;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also use parameters in reader queries exactly the same way as above. Also you can prepare the query and then set parameters/execute the query as a seperate step. Again the folloing are identical.&lt;br /&gt;&lt;pre&gt;$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @username;", false);&lt;br /&gt;$users-&amp;gt;username = $username;&lt;br /&gt;$users-&amp;gt;execute();&lt;br /&gt;&lt;br /&gt;$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @1;", false);&lt;br /&gt;$users-&amp;gt;execute(true, $username);&lt;br /&gt;&lt;br /&gt;$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @username;", false);&lt;br /&gt;$users-&amp;gt;execute(true, array(username =&amp;gt; $username);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To read the results there are a few other options as well.&lt;br /&gt;&lt;pre&gt;$users = new query("SELECT userID, username, lastAccess FROM users WHERE username = @username;", false);&lt;br /&gt;&lt;br /&gt;foreach($users as $user) {&lt;br /&gt;    $users-&amp;gt;username = $user;&lt;br /&gt;&lt;br /&gt;    if(!$users-&amp;gt;execute())&lt;br /&gt;        continus;&lt;br /&gt;&lt;br /&gt;    echo $users-&amp;gt;usersID; // Get a coulmn value.&lt;br /&gt;    print_r($users-&amp;gt;get_row()); // Print the entire row.&lt;br /&gt;    echo $users-&amp;gt;get_md5(); // Time dependent hash.&lt;br /&gt;    echo $users-&amp;gt;get_md5('userID', 'username'); // UserID/Username dependent hash.&lt;br /&gt;    echo $users-&amp;gt;get_columns(); // Get a list of loaded columns.&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Also there are a few other calls that may be useful. You can get the number or records and raw SQL statement like so.&lt;br /&gt;&lt;pre&gt;$users = new query("SELECT userID, username, lastAccess, enabled FROM users;");&lt;br /&gt;echo $users-&amp;gt;get_length();&lt;br /&gt;echo $users-&amp;gt;get_last_sql();&lt;br /&gt;&lt;br /&gt;query::run("UPDATE users SET groupName = @1, lastEdit = NOW() WHERE groupName = @2;", $newGroupName, $groupName);&lt;br /&gt;echo query::length();&lt;br /&gt;echo query::last_sql();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6581295589235786608?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6581295589235786608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6581295589235786608&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6581295589235786608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6581295589235786608'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/php-mysql-database-abstraction-class.html' title='PHP MySQL database abstraction class'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2140959412230272900</id><published>2010-04-16T13:46:00.000-04:00</published><updated>2010-04-16T13:46:04.495-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Backup your FreeBSD system configuration.</title><content type='html'>I set up a simple script to create a configuration backups of my FreeBSD box and I thought I would share it.  Note that this script will only back up the /etc and /usr/local/etc directories and weighs in at just under 1MB per backup.&lt;br /&gt;&lt;br /&gt;First create a backup script as we can't execute our complex command directly in cron.&amp;nbsp; You may want to customize the exclude options to your licking, the two listed exclusions are the rather large gconf defaults, witch is not needed, and the working files for transmission.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;sudo vim /usr/local/sbin/backup-config&lt;/b&gt;&lt;br /&gt;bash -c 'tar -czf /root/freebsd-cfg-`date "+%Y-%m-%d"`.tgz --exclude={etc/gconf,usr/local/etc/transmission/home/{resume,torrents,Downloads,blocklists}} /etc/ /usr/local/etc/'&lt;/pre&gt;&lt;br /&gt;Now make it executable.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;chmod +x /usr/local/sbin/backup-config&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Now add the job to cron and set it to run weekly as root.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;sudo vim /etc/crontab&lt;/b&gt;# Backup the entire server configuration once a week.&lt;br /&gt;0 1 * * 0 root backup-config 2&amp;gt;/dev/null&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2140959412230272900?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2140959412230272900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2140959412230272900&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2140959412230272900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2140959412230272900'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/backup-your-freebsd-system.html' title='Backup your FreeBSD system configuration.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-267395143382875124</id><published>2010-04-15T08:43:00.000-04:00</published><updated>2010-04-15T08:43:50.187-04:00</updated><title type='text'>Patch for VIrtualbox not working on Freebsd after updating graphics/png port.</title><content type='html'>I have had a very frustrating time over the last few days.&amp;nbsp; After doing a full portupgrade I found that virtualbox-ose would no longer work properly.&amp;nbsp; The GUI portion was working and I could run vm's but I was unable to manage any machines from the console with VBoxManage.&amp;nbsp; Trying to do any operations would just die with the following error.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ERROR: failed to create a session object!&lt;br /&gt;ERROR: code NS_ERROR_FACTORY_NOT_REGISTERED (0x80040154) - Class not&lt;br /&gt;registered (extended info not available)&lt;br /&gt;Most likely, the VirtualBox COM server is not running or failed to start.&lt;/pre&gt;&lt;br /&gt;The only changes where Virtualbox being bumped from 3.1.4 to 3.1.6 and a new version of the dependent graphics/png package.&amp;nbsp; After much testing I determined that the problem was with the png update change but I couldn't figure out how to resolve it.&amp;nbsp; I finally found a &lt;a href="http://lists.freebsd.org/pipermail/freebsd-emulation/2010-April/007632.html"&gt;patch&lt;/a&gt; out and about that fixed this problem so I am posting more details here.&amp;nbsp; The patch listed in that page is actually not a patch at all but rather a replacement makefile so i created a patch and posted instructions below.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Build the pached version&lt;/b&gt;&lt;br /&gt;&lt;a href="http://pynej.dnsalias.com/Shared/virtualbox-ose-3.1.6_2-1.patch"&gt;Patch&lt;/a&gt;,  build, and install.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cd /usr/ports/emulators/virtualbox-ose&lt;br /&gt;sudo wget http://pynej.dnsalias.com/Shared/virtualbox-ose-3.1.6_2-1.patch&lt;br /&gt;sudo patch -p0 &amp;lt; virtualbox-ose-3.1.6_2-1.patch&lt;br /&gt;sudo portupgrade -f virtualbox-ose&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-267395143382875124?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/267395143382875124/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=267395143382875124&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/267395143382875124'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/267395143382875124'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/patch-for-virtualbox-not-working-on.html' title='Patch for VIrtualbox not working on Freebsd after updating graphics/png port.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1133265543579285344</id><published>2010-04-08T16:54:00.000-04:00</published><updated>2010-04-08T16:54:40.703-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Updatting c# applicationSettings in a ASP.NET Web Application</title><content type='html'>I have a few .NET web applications, using MVC, that make use of applicationSettings in their configuration.&amp;nbsp; These settings are semi-constant but do need to be updated from time to time.&amp;nbsp; I was trying to make an edit screen in the web application for developers so they could edit the applicationSettings without having to get on the server and manually edit the Web.config file.&amp;nbsp; As expected the applicationSettings are read only when accesses directly in the application and can not be updated.&amp;nbsp; Also it's not possible to configure the settings as userSettings when running as a web application. &amp;nbsp; Though we could do this by manualy reading and writing the file I was  looking for a simpler way to do it.&lt;br /&gt;&lt;br /&gt;After some tinkering I found a fairly simple way of doing this.&amp;nbsp; Basically we can use a custom ConfigurationManager instance to read the Web.config independently of the application and update this instance of the configuration.&amp;nbsp; Then we just call the save method and the edited data is saved out.&amp;nbsp; Here is the code for a simple update call.&lt;br /&gt;&lt;br /&gt;Note that this code is tailored for MVC and is looping tough the FormCollection values.  You could also explicitly read the post variables as parameters of the post action, or use this outside of MVC entirely.  Just keep in mind that however you do it &lt;b&gt;you can't loop by clientSection.Settings&lt;/b&gt; as the requirement to remove/re-add each updated value prevents this.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;using System.Configuration;&lt;br /&gt;&lt;br /&gt;[AcceptVerbs(HttpVerbs.Post), Authorize(Roles = "Admin")]&lt;br /&gt;public ActionResult SaveSettings(FormCollection collection)&lt;br /&gt;{&lt;br /&gt;/* This section of code uses a custom configuration manager to edit the Web.config application settings.&lt;br /&gt; * These settings are normally read only but web apps don't support user scoped settings.&lt;br /&gt; * This set of variables is used for system features, not runtime tracking so it it only updated when an &lt;br /&gt; *  administrator logs in to reconfigure the system.&lt;br /&gt; * &lt;br /&gt; * Author: Jeremy Pyne &lt;jeremy.pyne@gmail.com&gt;&lt;br /&gt; * Licence: CC:BY/NC/SA  http://creativecommons.org/licenses/by-nc-sa/3.0/&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;// Load the Web.config file for editing.  A custom mapping to the file is needed as the default to to match the application's exe filename witch we don't have.&lt;br /&gt;System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() {ExeConfigFilename = HttpContext.Server.MapPath("..\\Web.config") }, ConfigurationUserLevel.None);&lt;br /&gt;&lt;br /&gt;// Find the applicationSettings group.&lt;br /&gt;ConfigurationSectionGroup group = config.SectionGroups["applicationSettings"];&lt;br /&gt;if (group == null)&lt;br /&gt;    throw new AjaxException("Could not find application settings.");&lt;br /&gt;&lt;br /&gt;// Find this applications section. Note: APP needs to be replaced with the namespace of your project.&lt;br /&gt;ClientSettingsSection clientSection = group.Sections["APP.Properties.Settings"] as ClientSettingsSection;&lt;br /&gt;if (clientSection == null)&lt;br /&gt;        throw new AjaxException("Could not find Hines settings.");&lt;br /&gt;&lt;br /&gt;// Loop through each value we are trying to update.&lt;br /&gt;foreach (string key in collection.AllKeys)&lt;br /&gt;{&lt;br /&gt;    // Look for a setting in the config that has the same name as the current variable.&lt;br /&gt;    SettingElement settingElement = clientSection.Settings.Get(key);&lt;br /&gt;&lt;br /&gt;    // Only update values that are present in the config file.&lt;br /&gt;    if (settingElement != null)&lt;br /&gt;    {&lt;br /&gt;        string value = collection[key];&lt;br /&gt;&lt;br /&gt;        // Is this is an xml value then we need to do some conversion instead.  This currently only supports the StringCollection class.&lt;br /&gt;        if (settingElement.SerializeAs == SettingsSerializeAs.Xml)&lt;br /&gt;        {&lt;br /&gt;            // Convert the form post (bob,apple,sam) to a StringCollection object.&lt;br /&gt;            System.Collections.Specialized.StringCollection sc = new System.Collections.Specialized.StringCollection();&lt;br /&gt;            sc.AddRange(value.Split(new char[] { ',' }));&lt;br /&gt;&lt;br /&gt;            // Make an XML Serilization of the new StringCollection&lt;br /&gt;            System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Specialized.StringCollection));&lt;br /&gt;            System.IO.StringWriter writer = new System.IO.StringWriter();&lt;br /&gt;            ser.Serialize(writer, sc);&lt;br /&gt;&lt;br /&gt;            // Get the xml code and trim the xml definition line from the top.&lt;br /&gt;            value = writer.ToString().Replace("&amp;lt;?xml version=\"1.0\" encoding=\"utf-16\"?&amp;gt;", "");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // This is a custom override for MVC checkboxes.  They post as 'false' when unchecked and 'true,false' when selected.&lt;br /&gt;        if(value == "true,false")&lt;br /&gt;            value = "True";&lt;br /&gt;        if(value == "false")&lt;br /&gt;            value = "False";&lt;br /&gt;&lt;br /&gt;        // Replace the settings with a updated settings.  It is necessary to do it this way instead of &lt;br /&gt;        //  updating it in place so that the configuration manager recognize that the setting has changed.&lt;br /&gt;        // Also we can't just look through clientSection.Settings and update that way because then we wouldn't&lt;br /&gt;        //  to do this exact thing.&lt;br /&gt;        clientSection.Settings.Remove(settingElement);&lt;br /&gt;        settingElement.Value.ValueXml.InnerXml = value;&lt;br /&gt;        clientSection.Settings.Add(settingElement);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Save any changes to the configuration file.  Don't set forceSaveAll or other parts of the Web.config will get overwritten and break.&lt;br /&gt;config.Save(ConfigurationSaveMode.Full);&lt;br /&gt;}&lt;/jeremy.pyne@gmail.com&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1133265543579285344?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1133265543579285344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1133265543579285344&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1133265543579285344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1133265543579285344'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/updatting-c-applicationsettings-in.html' title='Updatting c# applicationSettings in a ASP.NET Web Application'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3648156185707483816</id><published>2010-04-06T08:51:00.000-04:00</published><updated>2010-04-06T08:51:23.471-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><title type='text'>Multiple Google Calenders on the iPad</title><content type='html'>Ok, I was a bit frustrated after setting up the &lt;a href="http://www.google.com/support/mobile/bin/answer.py?answer=138740&amp;amp;topic=14252"&gt;Google Sync&lt;/a&gt; to find that I could only sync one calender even though the iPhone supports multiple.&amp;nbsp; I'm sure this will be fixed in short order by Google but until then here is how you can fix it on your desktop using Firefox.&amp;nbsp; You may also be able to do this in Safari as shown &lt;a href="http://jkontherun.com/2010/04/04/ipad-calendar-sync-fix-google/"&gt;here&lt;/a&gt; but I haven't tested that.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;First set up Google Sync as an Exchange account.&lt;/li&gt;&lt;li&gt;Then on you desktop install the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/59"&gt;User Agent Switcher&lt;/a&gt; extensions.&lt;/li&gt;&lt;li&gt;Restart Firefox and then go to Tools-&amp;gt;Default User Agent-&amp;gt;iPhone 3.0 to change your agent.&lt;/li&gt;&lt;li&gt;Now navigate to &lt;a href="http://m.google.com/sync"&gt;http://m.google.com/sync&lt;/a&gt; and select the iPad entry. &lt;/li&gt;&lt;li&gt;The new screen with list your calenders but the JavaScript code still prevents you from selecting multiple.&amp;nbsp; To get around this just go into the browser preferences and un-check Enable JavaScript under the Content tab.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Now you can select all the calenders you want and save the changes.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;To revert the changes to your browser first recheck the Enable JavaScript option and then change the User Agent back to Default User Agent.&lt;br /&gt;&lt;br /&gt;You can also just disable scripts temporarily on google.com with the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/722"&gt;NoScript&lt;/a&gt; plugin if you use it instead switching JavaScript on and off.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3648156185707483816?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3648156185707483816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3648156185707483816&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3648156185707483816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3648156185707483816'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/multiple-google-calenders-on-ipad.html' title='Multiple Google Calenders on the iPad'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1821401853188257491</id><published>2010-04-05T10:58:00.001-04:00</published><updated>2010-05-06T22:22:28.781-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Review'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='Apple'/><title type='text'>My thoughts on the iPad.</title><content type='html'>So I went to the apple store one Saturday to take a look at the new iPad's. &amp;nbsp;I hadn't pre-ordered one and wasn't sure if I would end up getting one rig away or wait for a newer model/price drop. &amp;nbsp;After playing with one for a bit and asking some questions I ended up buying the 16GB model with no accessories. What follows are my impressions of the device, mu thoughts on some of the criticisms, and some things I hope to see in the future.&lt;br /&gt;&lt;br /&gt;First off let me say hat I run OSX at home and have an first generation iPhone as well. &amp;nbsp;I like apple products for their reliability and usability. &amp;nbsp;I don't have a laptop so I was looking for a portable device that I could use at home and on the go for web surfing and personal uses. I get by with my phone for that now but it is rather slow when it comes to web browsing And the smaller screen limits some of it's usefulness. This is where I am coming from and what I was hoping to find with the iPad. &amp;nbsp;I found the iPad to be very responsive and the larger display to be stunning. &amp;nbsp;The Apple applications are top notch as one would expect though the third party apps are somewhat limited of of yet. &amp;nbsp;It is obvious that some developers have grasped the new user interface that Apple has created and some have not.&lt;br /&gt;&lt;br /&gt;As for some of the criticisms of the device, first off. It is not a laptop, it's not a net book, stop with the "you could get a laptop for cheaper". &amp;nbsp;Net book is cheaper yes, but a sub $500 net book is going to have a tiny screen, limited system resources, and run Windows. This type of device is in my mind a lot more hassle and a lot less useful then the iPad and iPhone OS. Furthermore if I were to get a full laptop it would be attest $1200 for a nice MacBook. Again I tend to avoid Windows and a $500-800 Windows laptop isn't going to have the same lifetime as my tablet. As for the lack of a camera, you wouldn't want to use this to take pictures and video conferencing would be nifty but not realistic. The iPad is much more useful as a tool during communications then a provider of it. I'm sure it is a feature they will add but Its just not a must have. &amp;nbsp;As far as background processing goes, put it to bed already. So the iPhone OS doesn't allow background processing. I'd rather have a stable and secure device then find out half way through the day my batter is dead because I left so e stupid app running in the background. The push notification system provide a lot of the user interaction with minimal power consumption. &amp;nbsp;The only service that I have found that would really benefit from background processing is instant messaging.&lt;br /&gt;&lt;br /&gt;As far as my hopes for the future there are a few things in the works that I expect in the 4.0 release and some third party apps I hope to see. Background processing for one is currently in testing for the 4.0 release. I would also like to see build in support for printing to network printers instead of needing third party apps. &amp;nbsp;Hulu will be a nice eater once they release their app as will some better google integration. As for things I hope to see. I would really like an app to view and even manage the shared iPhoto/Aperture libraries on my Mac. This would be nice so I Could play slideshows of all my photos without needing to sync over multiple gigabytes of photos. &amp;nbsp;The ability to manage the metadata of these libraries would also be a killer feature. The same thing would be nice for video content, that is the ability to browse the network and play video content back that did t come from iTunes, many even with xvid/mkv support. This is more of a pipe dream but I can hope. &amp;nbsp;Finally I hope to see a wireless sync/media streaming feature from Apple like the AppleTV supports and better controls over automatic downloads and updates of applications and subscriptions.&lt;br /&gt;&lt;br /&gt;All in all I like the iPad and am glad I bought it, though there are some minor problems and a lot of opportunities that can all be addressed with software updates and the rid part applications. As an aside I wrote this entire review and posted it from my iPad with minimal effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1821401853188257491?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1821401853188257491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1821401853188257491&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1821401853188257491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1821401853188257491'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/04/my-houghhts-on-ipad.html' title='My thoughts on the iPad.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7498210002127994246</id><published>2010-03-30T16:34:00.002-04:00</published><updated>2010-05-06T23:28:42.607-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><category scheme='http://www.blogger.com/atom/ns#' term='Photoshop'/><title type='text'>Doing Photoshop CS5 Content Aware Fill in GIMP</title><content type='html'>UPDATE: See the Photoshop CS5 version &lt;a href="http://pynej.blogspot.com/2010/05/adobe-photoshop-cs5-content-aware-fill.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Ok, so if you haven't seen the new Content Ware Fill feature in CS5 yet check it out &lt;a href="http://www.youtube.com/watch?v=NH0aEp1oDOI&amp;amp;feature=player_embedded"&gt;here&lt;/a&gt;.&amp;nbsp; I was looking into it and got a link to a GIMP plugin called &lt;a href="http://www.logarithmic.net/pfh/resynthesizer"&gt;Resynthesizer&lt;/a&gt;.&amp;nbsp; I decided to play around with it a bit and will compare the same edits in CS5 once its out.&amp;nbsp; Here are the two tests I did, the first was some small edits to remove a duck, some waves, and the power lines(and shadows).&amp;nbsp; The second was a larger edit to remove a big tree witch didn't go as well.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_b.jpg" width="320" /&gt;&lt;/a&gt;&lt;a href="http://farm5.static.flickr.com/4030/4477411066_53b7ccf1e0_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://farm5.static.flickr.com/4030/4477411066_53b7ccf1e0_o.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://farm4.static.flickr.com/3166/2923111412_aa50594c58_b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://farm4.static.flickr.com/3166/2923111412_aa50594c58_b.jpg" width="240" /&gt;&lt;/a&gt;&lt;a href="http://farm3.static.flickr.com/2521/4477410964_4907d7b040_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://farm3.static.flickr.com/2521/4477410964_4907d7b040_o.jpg" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-7498210002127994246?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7498210002127994246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=7498210002127994246&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7498210002127994246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7498210002127994246'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/03/doing-photoshop-cs5-content-aware-fill.html' title='Doing Photoshop CS5 Content Aware Fill in GIMP'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3231/2922259237_41b94ccaf9_t.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3286768823229451818</id><published>2010-03-25T14:46:00.001-04:00</published><updated>2010-04-15T08:37:01.265-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Mixing binary backage and source ports install sources with portupgrade in FreeBSD</title><content type='html'>Ok so a few weeks ago I made &lt;a href="http://pynej.blogspot.com/2010/03/setting-up-freebsd-to-auto-download-and.html"&gt;this post&lt;/a&gt; about automating update checks and downloads.&amp;nbsp; While as it turns out there was another issue I ran into.&amp;nbsp; The problem is simply this:&lt;br /&gt;&lt;br /&gt;When using portupgrade -Pa to to FreeBSD updates from packages all updated packages will be installed even if you compile one of the updated packages was compiled locally from the ports tree with custom options or libraries.&amp;nbsp; This causes any customizations to be lost and possibly version mismatches with system libraries.&lt;br /&gt;&lt;br /&gt;The only ways around this problem was to either only upgrade some subset of the ports or to set HOLD_PKGS and manually update problem ports after a package update.&amp;nbsp; Neither of these solutions is optimal so I took the time to make a patch for portupgrade.&lt;br /&gt;&lt;br /&gt;The patch adds a USE_PORTS configuration option that specifies a lit of packages to always build from the ports tree even when portupgrade -P is called.&amp;nbsp; (The -PP option still works the same as before.)&amp;nbsp; Thus any custom packages can be added to this list and then an automatic update can be done that will favor binary version when available and when not overridden.&amp;nbsp; This also means that if one package is built from source its parents and children will still come from binary packages if available.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Build the pached version&lt;/b&gt;&lt;br /&gt;&lt;a href="http://pynej.dnsalias.com/Shared/pkgtools-2.4.6-1.patch"&gt;Patch&lt;/a&gt;, build, and install.&lt;br /&gt;&lt;pre&gt;cd /usr/ports/ports-mgmt/portupgrade&lt;br /&gt;sudo make extract&lt;br /&gt;cd work&lt;br /&gt;sudo wget http://pynej.dnsalias.com/Shared/pkgtools-2.4.6-1.patch&lt;br /&gt;sudo patch -p0 &amp;lt; pkgtools-2.4.6-1.patch&lt;br /&gt;cd pkgtools-2.4.6&lt;br /&gt;sudo make isntall clean&lt;br /&gt;&lt;/pre&gt;You may also want to add portupgrade to your HOLD_PKGS list in /usr/local/etc/pkgtools.conf so it doesn't get replaced in the future.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Configuration&lt;/b&gt;&lt;br /&gt;To configure the list of overrides you will need to edit the following section: &lt;br /&gt;&lt;pre&gt;&lt;b&gt;sudo vim /usr/local/etc/pkgtools.conf&lt;/b&gt;&lt;br /&gt;USE_PORTS = [&lt;br /&gt;    &lt;br /&gt;  ]&lt;br /&gt;&lt;/pre&gt;Here is a sample of some common overrides.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;nbsp;php5 to enable the apache module&lt;/li&gt;&lt;li&gt;&amp;nbsp;php5-mysql to get the binding to the correct version of mysql-server&lt;/li&gt;&lt;li&gt;&amp;nbsp;mod_perl2 to enable apache module&lt;/li&gt;&lt;li&gt;&amp;nbsp;phpmyadmin as it depends ont he wrong php5-mysql paskage)  &lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;b&gt;sudo vim /usr/local/etc/pkgtools.conf&lt;/b&gt;&lt;br /&gt;USE_PORTS = [&lt;br /&gt;    'lang/php5',&lt;br /&gt;    'databases/php5-mysql',&lt;br /&gt;    'databases/phpmyadmin',&lt;br /&gt;    'www/mod_perl2',&lt;br /&gt;  ]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3286768823229451818?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3286768823229451818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3286768823229451818&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3286768823229451818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3286768823229451818'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/03/mixing-binary-backage-and-source-ports.html' title='Mixing binary backage and source ports install sources with portupgrade in FreeBSD'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2177058158761590411</id><published>2010-03-12T08:53:00.000-05:00</published><updated>2010-03-12T08:53:55.638-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='VirtualBox'/><title type='text'>Configure VirtualBox Deamon</title><content type='html'>A while back I switched from VMware to VirtualBox(when switching to freebsd) and ran into a problem getting virtual machines to start on server boot.  Virtual box supports background operation(headless mode) but freebsd does not come with and rc service for doing so.  Included below is a freebsd version to accomplish startup/shutdown task as well as other common operations.&lt;br /&gt;&lt;br /&gt;To install save this script into /usr/local/etc/rc.d/virtualbox and make it executable.  Then add the following lines to your /etc/rc.conf as needed.&lt;br /&gt;&lt;pre&gt;# Load the VirtualBox drivers and start any default machines.&lt;br /&gt;vboxnet_enable="YES" # Enable drivers from package virtualbox-ose-kmod.&lt;br /&gt;virtualbox_enable="YES"&lt;br /&gt;virtualbox_autostart="ASG" # List of machine names to start on boot.&lt;br /&gt;virtualbox_user="vboxuser"&lt;br /&gt;virtualbox_group="vboxusers"&lt;/pre&gt;&lt;br /&gt;A few other notes: I actually set up a separate account for VirtualBox and the daemon supports this.  Also please note that the autostart list is done by name and the names can not have spaces.  You can also manually start and stop machines with the startvm and stopvm options.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;/usr/local/etc/rc.d/virtualbox&lt;/b&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;# PROVIDE: virtualbox &lt;br /&gt;# REQUIRE: LOGIN vboxnet&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Add the following lines to /etc/rc.conf to enable the virtualbox daemon:&lt;br /&gt;#&lt;br /&gt;# virtualbox_enable="YES"&lt;br /&gt;# virtualbox_autostart="BOX1 BOX2"&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;. /etc/rc.subr&lt;br /&gt;&lt;br /&gt;name="virtualbox"&lt;br /&gt;rcvar=`set_rcvar`&lt;br /&gt;extra_commands="status startvm stopvm"&lt;br /&gt;&lt;br /&gt;start_cmd="${name}_start"&lt;br /&gt;stop_cmd="${name}_start"&lt;br /&gt;status_cmd="${name}_status"&lt;br /&gt;startvm_cmd="${name}_startvm"&lt;br /&gt;stopvm_cmd="${name}_stopvm"&lt;br /&gt;poweroffvm_cmd="${name}_poweroffvm"&lt;br /&gt;&lt;br /&gt;load_rc_config $name&lt;br /&gt;&lt;br /&gt;virtualbox_user=${virtualbox_user:-"vboxuser"}&lt;br /&gt;virtualbox_group=${virtualbox_group:-"vboxusers"}&lt;br /&gt;virtualbox_autostart=${virtualbox_autostart:-""}&lt;br /&gt;&lt;br /&gt;SU="su $virtualbox_user -c"&lt;br /&gt;VBOXMANAGE="/usr/local/bin/VBoxManage -nologo"&lt;br /&gt;&lt;br /&gt;virtualbox_start()&lt;br /&gt;{&lt;br /&gt;        echo "Starting ${name}."&lt;br /&gt;&lt;br /&gt;        echo $virtualbox_autostart | awk -F , '{for(i=1;i&lt;=NF;i++) print $i}' |  while read VM; do&lt;br /&gt;            $SU "$VBOXMANAGE startvm \"$VM\" --type headless" &lt;br /&gt;        done&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;virtualbox_stop()&lt;br /&gt;{&lt;br /&gt;        echo "Stopping ${name}."&lt;br /&gt;        $SU "$VBOXMANAGE list runningvms" | sed 's/.*"\(.*\)".*/\1/' | while read VM; do&lt;br /&gt;            $SU "$VBOXMANAGE controlvm \"$VM\" acpipowerbutton"&lt;br /&gt;        done&lt;br /&gt;&lt;br /&gt;        wait_for_closing_machines&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;virtualbox_startvm()&lt;br /&gt;{&lt;br /&gt;        $SU "$VBOXMANAGE startvm \"$*\" --type headless"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;virtualbox_stopvm()&lt;br /&gt;{&lt;br /&gt;        $SU "$VBOXMANAGE controlvm \"$*\" acpipowerbutton"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;virtualbox_poweroffvm()&lt;br /&gt;{&lt;br /&gt;        $SU "$VBOXMANAGE controlvm \"$*\" poweroff"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;virtualbox_status()&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;        $SU "$VBOXMANAGE list runningvms" | sed 's/.*"\(.*\)".*/\1/' | while read VM; do&lt;br /&gt;            echo "$VM "&lt;br /&gt;        done&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;wait_for_closing_machines() {&lt;br /&gt;    RUNNING_MACHINES=`$SU "$VBOXMANAGE list runningvms" | wc -l`&lt;br /&gt;    if [ $RUNNING_MACHINES != 0 ]; then&lt;br /&gt;        sleep 5&lt;br /&gt;        wait_for_closing_machines&lt;br /&gt;    fi&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;run_rc_command "$@"&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2177058158761590411?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2177058158761590411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2177058158761590411&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2177058158761590411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2177058158761590411'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/03/configure-virtualbox-deamon.html' title='Configure VirtualBox Deamon'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4281310503163463080</id><published>2010-03-05T09:04:00.001-05:00</published><updated>2010-03-11T15:35:11.166-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Setting up FreeBSD to Auto-Download and Notify Updates</title><content type='html'>&lt;b&gt;03/11/2010- Update: &lt;/b&gt; Updated the portsnap command to properly apply the port updates.  As configured before the changes would show up but the cron task to download packages would never download them.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;br /&gt;The default install of FreeBSD is very stable, and works well.  And it is relatively simple manually do updates, but I recent looked into setting up auto-updates like Ubuntu does.  The way I have it et up now is the following.&lt;br /&gt;&lt;br /&gt;Automatic downloading of kernel/world updates to the FreeBSD release.&lt;br /&gt;Automatic downloading and updating of the current ports tree.&lt;br /&gt;Automatic downloading of any updated binary packages of installed ports.&lt;br /&gt;&lt;br /&gt;These three tasks are set as cron jobs and run once a day/week to check for and download updates.  Reports are also sent to the root account on checking so you will be notified where updates are available.  The administrator can then manuals install the system updates, binary packages updates, and source port updates from the local cache.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setup&lt;/b&gt;&lt;br /&gt;First off we will add a few cron jobs to auto-download our updates.  Add these lines to /etc/cron and customize the run time as desired.&lt;br /&gt;&lt;pre&gt;# Check for freebsd updates, download them, and mail root.&lt;br /&gt;0       2       *       *       0       root    freebsd-update cron&lt;br /&gt;# Check for ports updates, download them, and mail root.&lt;br /&gt;0       3       *       *       0       root    portsnap cron update &amp;amp;&amp;amp; pkg_version -vIL=&lt;br /&gt;# Check for binary pacakge updates, download them, and mail root.&lt;br /&gt;0       4       *       *       0       root    portupgrade -PFa&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To enable the email reports you need to add an alias to send mail to forward root's mail to an administrator. To do so edit the file /etc/aliases and add line like so with your username.&lt;br /&gt;&lt;pre&gt;root: adminaccount&lt;/pre&gt;Then run the following command to make the change take effect.&lt;br /&gt;&lt;pre&gt;cd /etc/mail &amp;amp;&amp;amp; sudo make aliases&lt;/pre&gt;&lt;br /&gt;You will also need to install the portutils package if you don't have it for package updating.&lt;br /&gt;&lt;pre&gt;cd /usr/ports/ports-mgmt/portupgrade &amp;amp;&amp;amp; sudo make install&lt;/pre&gt;&lt;br /&gt;Once installed we need to change the package source location to pull binary package updates from the stable branch instead of the release branch.&amp;nbsp; The release packages are never updated and as such we would never find binary updates. To change this edit the /usr/local/etc/pkgtools.conf file and change the  PKG_SITES variable to the following.&lt;br /&gt;&lt;pre&gt;PKG_SITES = [&lt;br /&gt;    sprintf('ftp://ftp.freebsd.org/pub/FreeBSD/ports/%s/packages-%s-stable/', OS_PLATFORM, OS_MAJOR)  &lt;br /&gt;  ]&lt;/pre&gt;&lt;br /&gt;Unfortunately the&amp;nbsp; portupgrade utility does not respect packages you customized and build by hand and will just overwrite them with the binary version.&amp;nbsp; To get around this you can add any exceptions you want to HOLD_PKGS array in this file and update them manually.&amp;nbsp; You way also want to add any languages you don't use to the IGNORE_CATEGORIES array at this time as well to speed up the ports commands.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Manual Update&lt;/b&gt;&lt;br /&gt;Once all these steps are done we can force a manual update of all three with the following commands, though they will take a bit to complete.&lt;br /&gt;&lt;pre&gt;sudo freebsd-update fetch&lt;br /&gt;sudo portsnap fetch update&lt;br /&gt;sudo portupgrade -PFa&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Installing Updates&lt;/b&gt;&lt;br /&gt;If using ZFS you may want to make a snapshot first.&lt;br /&gt;&lt;pre&gt;sudo zfs snapshot zroot@ver-date&lt;br /&gt;sudo zfs snapshot zroot/usr@ver-date&lt;/pre&gt;&lt;br /&gt;When you want to do an actual update to the system here are the commands to install the downloaded updates.&lt;br /&gt;&lt;pre&gt;sudo freebsd-update install&lt;br /&gt;sudo portupgrade -Pa&lt;/pre&gt;&lt;br /&gt;And finally its a good idea to clean out the old files manually or via another cron task..&lt;br /&gt;&lt;pre&gt;portsclean -CDPL&lt;/pre&gt;&lt;br /&gt;If everything went smoothly you may wish to remove the old snapshots.&lt;br /&gt;&lt;pre&gt;sudo zfs destroy zroot@ver-date&lt;br /&gt;sudo zfs destroy zroot/usr@ver-date&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4281310503163463080?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4281310503163463080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4281310503163463080&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4281310503163463080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4281310503163463080'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/03/setting-up-freebsd-to-auto-download-and.html' title='Setting up FreeBSD to Auto-Download and Notify Updates'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-639758331608132640</id><published>2010-02-23T10:25:00.001-05:00</published><updated>2010-02-24T14:04:52.519-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Set up the transmission-deamon BitTorrent Client in FreeBSD</title><content type='html'>I switched over to FreeBSD a while back and still am in the process of configuring it the way my Ubuntu server used to be set up.  One of the things I used to have was uTorrent running, via wine, and auto-loading torrents from a shared directory.  This allowed me to queue downloads from anywhere and have them auto-download.  It is not possible to set up my 32bit FreeBSD box the same was as ZFS prevents wine from operating. (This is a known issue due to the custom kernel with an increased memory map.) &lt;br /&gt;&lt;br /&gt;As such I was looking for a similar solution that would run as a daemon and could automatically process torrent files.  And as it turns out the transmission-daemon package does exactly that.  Following is a brief review of setting up this service and some customizations I did.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setup&lt;/b&gt;&lt;br /&gt;I installed the transmission-daemon package via the ports tree.&lt;br /&gt;&lt;pre&gt;cd /usr/ports/net-p2p/transmission-daemon&lt;br /&gt;sudo portinstall -P&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Daemon Configuration&lt;/b&gt;&lt;br /&gt;I created my own /usr/local/srv/torrent directory and set up transmission to use it.  This path is also shared out to the network via NFS.&lt;br /&gt;&lt;pre&gt;sudo -u transmission mkdir -p /usr/local/srv/torrent/{data,autoload}&lt;br /&gt;chmod -R g+w /usr/local/srv/torrent&lt;/pre&gt;&lt;br /&gt;I added the following to my rc.conf so that transmission would use the new paths.&lt;br /&gt;&lt;pre&gt;sudo vim /etc/rc.conf&lt;br /&gt;  transmission_enable="YES"&lt;br /&gt;  transmission_watch_dir="/usr/local/srv/torrent/autoload"&lt;br /&gt;  transmission_download_dir="/usr/local/srv/torrent/data"&lt;/pre&gt;&lt;br /&gt;You have to start and then stop  transmission so it will create its configuration files&lt;br /&gt;&lt;pre&gt;sudo /usr/local/etc/rc.d/transmission --start&lt;br /&gt;sudo /usr/local/etc/rc.d/transmission --stop&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Transmission Configuration&lt;/b&gt;&lt;br /&gt;Now edit the  transmission configuration file to change a few settings.  Only the settings listed below need to be changed now, the rest can be managed after the daemon is up and running with the transmission-remote command.  The first few settings force authentication and add the local subnet to the white list for remote access.  The blacklist setting will cause transmission to ignore any hosts in the optional blacklist file.  The last settings changes the file creation mask so that members of the  transmission group can manage the downloads.&lt;br /&gt;&lt;pre&gt;sudo -u transmission vim /usr/local/etc/transmission/home/settings.json&lt;br /&gt;  "rpc-authentication-required": true,&lt;br /&gt;  "rpc-username": "admin",&lt;br /&gt;  "rpc-password": "password",&lt;br /&gt;  "rpc-whitelist": "127.0.0.1,192.168.1.*",&lt;br /&gt;  "blocklist-enabled": true,&lt;br /&gt;  "umask": 2,&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Blacklist Configuration&lt;/b&gt;&lt;br /&gt;You can fetch a copy of the peer blacklist with the following command.&lt;br /&gt;&lt;pre&gt;sudo -u transmission wget -NP /usr/local/etc/transmission/home/blocklists http://update.transmissionbt.com/level1.gz&lt;/pre&gt;&lt;br /&gt;And add the following line to sron so it updates every night.&lt;br /&gt;&lt;pre&gt;sudo -u transmission crontab -e&lt;br /&gt;  0 0 * * * /usr/local/bin/wget -NP ~/blocklists http://update.transmissionbt.com/level1.gz &amp;&amp; gunzip -f ~/bl&lt;br /&gt;ocklists/level1.gz&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Final Steps&lt;/b&gt;&lt;br /&gt;You will also want to add your user to the transmission group so that you can directly manage downloads.&lt;br /&gt;&lt;pre&gt;sudo pw usermod USERNAME -G  transmission&lt;/pre&gt;&lt;br /&gt;The configureation is all don and we can start the server up for real now.&lt;br /&gt;&lt;pre&gt;sudo /usr/local/etc/rc.d/transmission --start&lt;/pre&gt;&lt;br /&gt;You can now tweak the torrent server properties as necessary with the following command&lt;br /&gt;&lt;pre&gt;transmission-remote --auth admin:password&lt;/pre&gt;&lt;br /&gt;You can also view and manage torrents from the web interface at &lt;a href="http://localhost:9091"&gt;http://localhost:9091&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Also this &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/13854"&gt;Firefox add-on&lt;/a&gt;  can be used to add torrents directly to transmission from any computer on the network instead of using the network share.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-639758331608132640?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/639758331608132640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=639758331608132640&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/639758331608132640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/639758331608132640'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/02/set-up-transmission-deamon-bittorrent.html' title='Set up the transmission-deamon BitTorrent Client in FreeBSD'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3338816702356145063</id><published>2010-02-16T16:53:00.003-05:00</published><updated>2010-02-23T09:12:30.290-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><title type='text'>Convert XML to Array and Array to XML in PHP</title><content type='html'>This class library provides two XML related functions.&amp;nbsp; One to convert an XML Tree into a PHP array and another to convert a complex PHP object into XML.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The first is the &lt;b&gt;xml_to_array&lt;/b&gt; function witch will read through an entire xml tree and convert it into a single multi-dimensional array.&amp;nbsp; That is each node in the root node will be added as items in the array and all children and properties will be added to the respective items.&amp;nbsp; This is done in a completely recursive manner so that nodes within nodes within nodes will be fully read and created as arrays within arrays within arrays.&amp;nbsp; The exact format of the generated array is dependent on the options selected.&lt;br /&gt;&lt;br /&gt;The default options will merge all the properties and children directly into the node but this may need to be disabled if you have children with the same name as properties or if you need to distinguish between the two types.&lt;br /&gt;&lt;br /&gt;Here is a sample XML file, from a svn dump.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;svn log --non-interactive --xml&lt;/b&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;log&amp;gt;&lt;br /&gt;&amp;lt;logentry&lt;br /&gt;   revision="114"&amp;gt;&lt;br /&gt;&amp;lt;author&amp;gt;pynej&amp;lt;/author&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2010-01-11T14:20:42.200771Z&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;msg&amp;gt;Removed smarty 2.0 code.&lt;br /&gt;&amp;lt;/msg&amp;gt;&lt;br /&gt;&amp;lt;/logentry&amp;gt;&lt;br /&gt;&amp;lt;logentry&lt;br /&gt;   revision="113"&amp;gt;&lt;br /&gt;&amp;lt;author&amp;gt;pynej&amp;lt;/author&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2008-08-08T05:14:31.046815Z&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;msg&amp;gt;* Changed videos to search recursively.  This may be configured by an extra variable in the future.&amp;lt;/msg&amp;gt;&lt;br /&gt;&amp;lt;/logentry&amp;gt;&lt;br /&gt;&amp;lt;/log&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Calling the conversion with the default options would look like so.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;print_r(xmlutils::xml_to_array($xml));&lt;/b&gt;&lt;br /&gt;Array&lt;br /&gt;(&lt;br /&gt;    [name] =&amp;gt; log&lt;br /&gt;    [0] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [name] =&amp;gt; logentry&lt;br /&gt;            [revision] =&amp;gt; 114&lt;br /&gt;            [author] =&amp;gt; pynej&lt;br /&gt;            [date] =&amp;gt; 2010-01-11T14:20:42.200771Z&lt;br /&gt;            [msg] =&amp;gt; Removed smarty 2.0 code.&lt;br /&gt;        )&lt;br /&gt;&lt;br /&gt;    [1] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [name] =&amp;gt; logentry&lt;br /&gt;            [revision] =&amp;gt; 113&lt;br /&gt;            [author] =&amp;gt; pynej&lt;br /&gt;            [date] =&amp;gt; 2008-08-08T05:14:31.046815Z&lt;br /&gt;            [msg] =&amp;gt; * Changed videos to search recursively.  This may be configured by an extra variable in the future.&lt;br /&gt;        )&lt;br /&gt;)&lt;/pre&gt;&lt;br /&gt;Where as the call with no options generates the more detailed but less user friendly output. &lt;br /&gt;&lt;pre&gt;&lt;b&gt;print_r(xmlutils::xml_to_array($xml), 0);&lt;/b&gt;&lt;br /&gt;Array&lt;br /&gt;(&lt;br /&gt;    [name] =&amp;gt; log&lt;br /&gt;    [children] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [0] =&amp;gt; Array&lt;br /&gt;                (&lt;br /&gt;                    [name] =&amp;gt; logentry&lt;br /&gt;                    [attributes] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [revision] =&amp;gt; 114&lt;br /&gt;                        )&lt;br /&gt;                    [children] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [0] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; author&lt;br /&gt;                                    [value] =&amp;gt; pynej&lt;br /&gt;                                )&lt;br /&gt;                            [1] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; date&lt;br /&gt;                                    [value] =&amp;gt; 2010-01-11T14:20:42.200771Z&lt;br /&gt;                                )&lt;br /&gt;                            [2] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; msg&lt;br /&gt;                                    [value] =&amp;gt; Removed smarty 2.0 code.&lt;br /&gt;                                )&lt;br /&gt;                        )&lt;br /&gt;                )&lt;br /&gt;&lt;br /&gt;            [1] =&amp;gt; Array&lt;br /&gt;                (&lt;br /&gt;                    [name] =&amp;gt; logentry&lt;br /&gt;                    [attributes] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [revision] =&amp;gt; 113&lt;br /&gt;                        )&lt;br /&gt;                    [children] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [0] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; author&lt;br /&gt;                                    [value] =&amp;gt; pynej&lt;br /&gt;                                )&lt;br /&gt;                            [1] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; date&lt;br /&gt;                                    [value] =&amp;gt; 2008-08-08T05:14:31.046815Z&lt;br /&gt;                                )&lt;br /&gt;                            [2] =&amp;gt; Array&lt;br /&gt;                                (&lt;br /&gt;                                    [name] =&amp;gt; msg&lt;br /&gt;                                    [value] =&amp;gt; * Changed videos to search recursively.  This may be configured by an extra variable in the future.&lt;br /&gt;                                )&lt;br /&gt;                        )&lt;br /&gt;                )&lt;br /&gt;        )&lt;br /&gt;) &lt;/pre&gt;&lt;br /&gt;A more useful format might be to retail the attribute/children breakdown but processes the rest.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;print_r(xmlutils::xml_to_array($xml), xmlutils::XML_MERGE_ATTRIBUTES | xmlutils::XML_VALUE_PAIRS | xmlutils::XML_MERGE_VALUES);&lt;/b&gt;&lt;br /&gt;Array&lt;br /&gt;(&lt;br /&gt;    [name] =&amp;gt; log&lt;br /&gt;    [children] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [0] =&amp;gt; Array&lt;br /&gt;                (&lt;br /&gt;                    [name] =&amp;gt; logentry&lt;br /&gt;                    [revision] =&amp;gt; 114&lt;br /&gt;                    [children] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [author] =&amp;gt; pynej&lt;br /&gt;                            [date] =&amp;gt; 2010-01-11T14:20:42.200771Z&lt;br /&gt;                            [msg] =&amp;gt; Removed smarty 2.0 code.&lt;br /&gt;                        )&lt;br /&gt;                )&lt;br /&gt;&lt;br /&gt;            [1] =&amp;gt; Array&lt;br /&gt;                (&lt;br /&gt;                    [name] =&amp;gt; logentry&lt;br /&gt;                    [revision] =&amp;gt; 113&lt;br /&gt;                    [children] =&amp;gt; Array&lt;br /&gt;                        (&lt;br /&gt;                            [author] =&amp;gt; pynej&lt;br /&gt;                            [date] =&amp;gt; 2008-08-08T05:14:31.046815Z&lt;br /&gt;                            [msg] =&amp;gt; * Changed videos to search recursively.  This may be configured by an extra variable in the future.&lt;br /&gt;                        )&lt;br /&gt;                )&lt;br /&gt;        )&lt;br /&gt;) &lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;The second function &lt;b&gt;array_to_xml&lt;/b&gt; will to the inverse of this and will generate a XML Tree from a complex anonymous array object.&amp;nbsp; This object can contain any amount of data and will be recursively traversed and added to the tree.&lt;br /&gt;&lt;br /&gt;Also note that XML Tree's cant have shared nodes or references so if any exist in the source object the data in them will be duplicated in the XML Tree.&amp;nbsp; There is no way to preserve these references with this tool.&amp;nbsp; Also note that no recursion checks are done so an object with circular recursion will lock up the process.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The following array converted to XML will look like so.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;print_r($logList);&lt;/b&gt;&lt;br /&gt;Array&lt;br /&gt;(&lt;br /&gt;    [name] =&amp;gt; log&lt;br /&gt;    [0] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [name] =&amp;gt; logentry&lt;br /&gt;            [revision] =&amp;gt; 114&lt;br /&gt;            [author] =&amp;gt; pynej&lt;br /&gt;            [date] =&amp;gt; 2010-01-11T14:20:42.200771Z&lt;br /&gt;            [msg] =&amp;gt; Removed smarty 2.0 code.&lt;br /&gt;        )&lt;br /&gt;&lt;br /&gt;    [1] =&amp;gt; Array&lt;br /&gt;        (&lt;br /&gt;            [name] =&amp;gt; logentry&lt;br /&gt;            [revision] =&amp;gt; 113&lt;br /&gt;            [author] =&amp;gt; pynej&lt;br /&gt;            [date] =&amp;gt; 2008-08-08T05:14:31.046815Z&lt;br /&gt;            [msg] =&amp;gt; Array&lt;br /&gt;                (&lt;br /&gt;                    [0] =&amp;gt; * Added links to the debug section to view the contents of ajax calls.&lt;br /&gt;                    [1] =&amp;gt; * Added query-&amp;gt;get_md5 to calculate the md5 hash of a query result.&lt;br /&gt;        )&lt;br /&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;print_r(xmlutils::array_to_xml($logList));&lt;/b&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt; &amp;lt;name&amp;gt;log&amp;lt;/name&amp;gt;&lt;br /&gt; &amp;lt;data-item&amp;gt;&lt;br /&gt;  &amp;lt;name&amp;gt;logentry&amp;lt;/name&amp;gt;&lt;br /&gt;  &amp;lt;revision&amp;gt;114&amp;lt;/revision&amp;gt;&lt;br /&gt;  &amp;lt;author&amp;gt;pynej&amp;lt;/author&amp;gt;&lt;br /&gt;  &amp;lt;date&amp;gt;2010-01-11T14:20:42.200771Z&amp;lt;/date&amp;gt;&lt;br /&gt;  &amp;lt;msg&amp;gt;Removed smarty 2.0 code.&amp;lt;/msg&amp;gt;&lt;br /&gt; &amp;lt;/data-item&amp;gt;&lt;br /&gt; &amp;lt;data-item&amp;gt;&lt;br /&gt;  &amp;lt;name&amp;gt;logentry&amp;lt;/name&amp;gt;&lt;br /&gt;  &amp;lt;revision&amp;gt;113&amp;lt;/revision&amp;gt;&lt;br /&gt;  &amp;lt;author&amp;gt;pynej&amp;lt;/author&amp;gt;&lt;br /&gt;  &amp;lt;date&amp;gt;2008-08-08T05:14:31.046815Z&amp;lt;/date&amp;gt;&lt;br /&gt;  &amp;lt;msg&amp;gt;&lt;br /&gt;    &amp;lt;msg-item&amp;gt;* Added links to the debug section to view the contents of ajax calls.&amp;lt;/msg-item&amp;gt;&lt;br /&gt;    &amp;lt;msg-item&amp;gt;* Added query-&amp;gt;get_md5 to calculate the md5 hash of a query result.&amp;lt;/msg-item&amp;gt;&lt;br /&gt;  &amp;lt;/msg&amp;gt;&lt;br /&gt; &amp;lt;/data-item&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;Source Code:&lt;br /&gt;&lt;pre&gt;&lt;b&gt;xmlutils.php&lt;/b&gt;&lt;br /&gt;/**&lt;br /&gt; * This class contains mothods to convert a xml tree into a complex array with nested properties and to convert a complex array object into an xml tree.&lt;br /&gt; *&lt;br /&gt; * @package xml-utils&lt;br /&gt; * @author Jeremy Pyne &amp;lt;jeremy.pyne@gmail.com&amp;gt;&lt;br /&gt; * @license CC:BY/NC/SA  http://creativecommons.org/licenses/by-nc-sa/3.0/&lt;br /&gt; * @lastupdate February 16 2010&lt;br /&gt; * @version 1.5&lt;br /&gt; */&lt;br /&gt;final class xmlutils&lt;br /&gt;{&lt;br /&gt;       /**&lt;br /&gt;         * Add a levels attributes directly to the levels node instead of into an attributes array.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_MERGE_ATTRIBUTES = 1;&lt;br /&gt;        /**&lt;br /&gt;         * Merge the attribures of a level into the parent level that they belong to.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_MERGE_VALUES = 2;&lt;br /&gt;        /**&lt;br /&gt;         * Add a levels children directly to the levels node instead of into a children array.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_MERGE_CHIILDREN = 4;&lt;br /&gt;        /**&lt;br /&gt;         * Process value as a lone entry under their level and ignore the other attributes and children.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_VALUE_PAIRS = 8;&lt;br /&gt;        /**&lt;br /&gt;         * Split the value of a node into an array on newlines.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_SPLIT_VALUES = 16;&lt;br /&gt;        /**&lt;br /&gt;         * If a value is an array with a single item, just use the item.&lt;br /&gt;         *&lt;br /&gt;         */&lt;br /&gt;        const XML_SPLIT_SHIFT = 32;&lt;br /&gt;&lt;br /&gt;        /**&lt;br /&gt;         * This function will convert an XML tree into a multi-dimentional array.&lt;br /&gt;         *&lt;br /&gt;         * @param SimpleXMLElement $xml&lt;br /&gt;         * @param bitfield $ops&lt;br /&gt;         * @return array&lt;br /&gt;         */&lt;br /&gt;        public static function xml_to_array($xml, $ops=63) {&lt;br /&gt;                // Store the name of this level.&lt;br /&gt;                $level = array();&lt;br /&gt;                $level["name"] = $xml-&amp;gt;getName();&lt;br /&gt;&lt;br /&gt;                // Grab the value of this level.&lt;br /&gt;                $value = trim((string)$xml);&lt;br /&gt;&lt;br /&gt;                // If we have a value, process it.&lt;br /&gt;                if($value) {&lt;br /&gt;                        // Split the value into an array on newlines.&lt;br /&gt;                        if($ops &amp; self::XML_SPLIT_VALUES)&lt;br /&gt;                                $value = explode("\n", $value);&lt;br /&gt;&lt;br /&gt;                        // If the value is an array with one item, remove the array.&lt;br /&gt;                        if($ops &amp; self::XML_SPLIT_SHIFT)&lt;br /&gt;                                if(sizeof($value) == 1)&lt;br /&gt;                                        $value = array_shift($value);&lt;br /&gt;&lt;br /&gt;                        // Store the value of this level.&lt;br /&gt;                        $level["value"] = $value;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // If this level had a value just return the name/value as an array.&lt;br /&gt;                if($ops &amp; self::XML_VALUE_PAIRS &amp;&amp; array_key_exists("value", $level))&lt;br /&gt;                        return array($level["name"] =&amp;gt; $level["value"]);&lt;br /&gt;&lt;br /&gt;                // Loop through each atribute of this level.&lt;br /&gt;                foreach($xml-&amp;gt;attributes() as $attribute) {&lt;br /&gt;                        // Add each attribure directly to this level in the array.&lt;br /&gt;                        if($ops &amp; self::XML_MERGE_ATTRIBUTES)&lt;br /&gt;                                $level[$attribute-&amp;gt;getName()] = (string)$attribute;&lt;br /&gt;                        // Add all the attributes to an attributes array under this level in the array.&lt;br /&gt;                        else&lt;br /&gt;                                $level["attributes"][$attribute-&amp;gt;getName()] = (string)$attribute;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // Loop through each child of this level.&lt;br /&gt;                foreach($xml-&amp;gt;children() as $children) {&lt;br /&gt;                        // Get an array of this childs data.&lt;br /&gt;                        $child = self::xml_to_array($children, $ops);&lt;br /&gt;&lt;br /&gt;                        if($ops &amp; self::XML_MERGE_VALUES) {&lt;br /&gt;                                // Add each child directly to this level  or to the children array of this level in the array.&lt;br /&gt;                                if(sizeof($child) == 1) {&lt;br /&gt;                                        // If there is only one child then merge it up.&lt;br /&gt;                                        if($ops &amp; self::XML_MERGE_CHIILDREN)&lt;br /&gt;                                                $level[array_shift(array_keys($child))] = $child[array_shift(array_keys($child))];&lt;br /&gt;                                        else&lt;br /&gt;                                                $level["children"][array_shift(array_keys($child))] = $child[array_shift(array_keys($child))];&lt;br /&gt;                                } elseif(array_key_exists("value", $child)) {&lt;br /&gt;                                        // If there is a value key then merge it up.&lt;br /&gt;                                        if($ops &amp; self::XML_MERGE_CHIILDREN)&lt;br /&gt;                                                $level[$child["name"]] = $child["value"];&lt;br /&gt;                                        else&lt;br /&gt;                                                $level["children"][$child["name"]] = $child["value"];&lt;br /&gt;                                } elseif(array_key_exists("children", $child)) {&lt;br /&gt;                                        // If there are children, then merge them up.&lt;br /&gt;                                        if($ops &amp; self::XML_MERGE_CHIILDREN)&lt;br /&gt;                                                $level[] = $child;&lt;br /&gt;                                        else&lt;br /&gt;                                                $level["children"][] = $child;&lt;br /&gt;                                } else {&lt;br /&gt;                                        // Otherwise just assigne yourself.&lt;br /&gt;                                        if($ops &amp; self::XML_MERGE_CHIILDREN)&lt;br /&gt;                                                $level[] = $child;&lt;br /&gt;                                        else&lt;br /&gt;                                                $level["children"][] = $child;&lt;br /&gt;                                }&lt;br /&gt;                        } else {&lt;br /&gt;                                $level["children"][] = $child;&lt;br /&gt;                        }&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                return $level;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /**&lt;br /&gt;        * The main function for converting to an XML document.&lt;br /&gt;        * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.&lt;br /&gt;        *&lt;br /&gt;        * @param array $data&lt;br /&gt;        * @param string $rootNodeName - what you want the root node to be - defaultsto data.&lt;br /&gt;        * @param SimpleXMLElement $xml - should only be used recursively&lt;br /&gt;        * @return string XML&lt;br /&gt;        */&lt;br /&gt;        public static function array_to_xml($data, $rootNodeName = 'data', $xml=null, $parentXml=null)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;                // turn off compatibility mode as simple xml throws a wobbly if you don't.&lt;br /&gt;                if (ini_get('zend.ze1_compatibility_mode') == 1)&lt;br /&gt;                {&lt;br /&gt;                        ini_set ('zend.ze1_compatibility_mode', 0);&lt;br /&gt;                }&lt;br /&gt;                //if ($rootNodeName == false) {&lt;br /&gt;                //      $xml = simplexml_load_string("&amp;lt;s/&amp;gt;");&lt;br /&gt;                //}&lt;br /&gt;                if ($xml == null)&lt;br /&gt;                {&lt;br /&gt;                       $xml = simplexml_load_string("&amp;lt;?xml version='1.0' encoding='utf-8'?&amp;gt;&amp;lt;$rootNodeName /&amp;gt;");&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // loop through the data passed in.&lt;br /&gt;                foreach($data as $key =&amp;gt; $value)&lt;br /&gt;                {&lt;br /&gt;                        // Create a name for this item based off the attribute name or if this is a item in an array then the parent nodes name.&lt;br /&gt;                        $nodeName = is_numeric($key) ? $rootNodeName . '-item' : $key;&lt;br /&gt;                        $nodeName = preg_replace('/[^a-z1-9_-]/i', '', $nodeName);&lt;br /&gt;&lt;br /&gt;                        // If this item is an array then we will be recursine to the logic is more complex.&lt;br /&gt;                        if (is_array($value)) {&lt;br /&gt;                                // If this node is part of an array we have to proccess is specialy.&lt;br /&gt;                                if (is_numeric($key)) {&lt;br /&gt;                                        // Another exception if this is teh root node and is an array.  In this case we don't have a parent node to use so we must use the current node and not update the reference. &lt;br /&gt;                                        if($parentXml == null) {&lt;br /&gt;                                                $childXml = $xml-&amp;gt;addChild($nodeName);&lt;br /&gt;                                                self::array_to_xml($value, $nodeName, $childXml, $xml);&lt;br /&gt;                                        // If this is a array node then we want to add the item under the parent node instead of out current node. Also we have to update $xml to reflect the change.&lt;br /&gt;                                        } else {&lt;br /&gt;                                                $xml = $parentXml-&amp;gt;addChild($nodeName);&lt;br /&gt;                                                self::array_to_xml($value, $nodeName, $xml, $parentXml);&lt;br /&gt;                                        }&lt;br /&gt;                                } else {&lt;br /&gt;                                        // For a normal attribute node just add it to the parent node.&lt;br /&gt;                                        $childXml = $xml-&amp;gt;addChild($nodeName);&lt;br /&gt;                                        self::array_to_xml($value, $nodeName, $childXml, $xml);&lt;br /&gt;                                }&lt;br /&gt;                        // If not then it is a simple value and can be directly appended to the XML tree.&lt;br /&gt;                        } else {&lt;br /&gt;                                $value = htmlentities($value);&lt;br /&gt;                                $xml-&amp;gt;addChild($nodeName, $value);&lt;br /&gt;                        }&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // Pass back as string or simple xml object.&lt;br /&gt;                return $xml-&amp;gt;asXML();&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3338816702356145063?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3338816702356145063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3338816702356145063&amp;isPopup=true' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3338816702356145063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3338816702356145063'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/02/convert-xml-to-array-and-array-to-xml.html' title='Convert XML to Array and Array to XML in PHP'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1916381893164250464</id><published>2010-02-10T15:45:00.001-05:00</published><updated>2011-03-04T08:26:13.718-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Find Duplicate Files in the Terminal</title><content type='html'>I posted an Automator Service last week for &lt;a href="http://pynej.blogspot.com/2010/01/find-duplicate-photos-in-iphoto-library.html"&gt;finding duplicate photo's in an iPhoto Library&lt;/a&gt;.&amp;nbsp; Here is a slightly modified version of the internal script it uses. You can save this script and run it in a terminal to find duplicate file of any kind in any directory tree of your choice.&amp;nbsp; This can also be included in Automater actions itself with the Shell Script action.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;findDuplicates.pl&lt;/b&gt;&lt;br /&gt;#!/usr/bin/perl&lt;br /&gt;&lt;br /&gt;# ##################################### #&lt;br /&gt;# Filename:      findDuplicates.pl&lt;br /&gt;# Author:        Jeremy Pyne&lt;br /&gt;# Licence:       CC:BY/NC/SA  http://creativecommons.org/licenses/by-nc-sa/3.0/&lt;br /&gt;# Last Update:   02/10/2010&lt;br /&gt;# Version:       1.5&lt;br /&gt;# Requires:      perl&lt;br /&gt;# Description:&lt;br /&gt;#   This script will look through a directory of files and find and duplicates.  It will then&lt;br /&gt;#   return a list of any such duplicates it finds.  This is done by calculating the md5 checksum&lt;br /&gt;#   of each file and recording it along with the filename.  Then the list is sorted by the checksum&lt;br /&gt;#   and read in line by line.  Any time multiple records in a row share a checksum the file names&lt;br /&gt;#   are written out to stdout.  As a result all empty files will be flagged as duplicates as well.&lt;br /&gt;# ##################################### #&lt;br /&gt;&lt;br /&gt;# Get the path from the command line.  Thos could be expanded to provide more granular control.&lt;br /&gt;$dir = shift;&lt;br /&gt;&lt;br /&gt;# Set up the location of the temp files.&lt;br /&gt;$file = "/tmp/pictures.txt";&lt;br /&gt;$sort = "/tmp/sorted.txt";&lt;br /&gt;&lt;br /&gt;# Find all files in the selected directory and calculate their md5sum.  This is by far the longest step.&lt;br /&gt;`find "$dir" -type file -print0 | xargs -0 md5 -r &amp;gt; $file`;&lt;br /&gt;# Sort the resulting file by the md5sum's.&lt;br /&gt;`sort $file &amp;gt; $sort`;&lt;br /&gt;&lt;br /&gt;open FILE, "&amp;lt;$sort" or die $!;&lt;br /&gt;&lt;br /&gt;my $newmd5;&lt;br /&gt;my $newfile;&lt;br /&gt;my $lastmd5;&lt;br /&gt;my $lastfile;&lt;br /&gt;my $lastprint = 0;&lt;br /&gt;&lt;br /&gt;# Read each line fromt he file.&lt;br /&gt;while(&lt;file&gt;) {&lt;br /&gt;        # Extract the md5sum and the filename.&lt;br /&gt;        $_ =~ /([^ ]+) (.+)/;&lt;br /&gt;&lt;br /&gt;        $newmd5 = $1;&lt;br /&gt;        $newfile = $2;&lt;br /&gt;&lt;br /&gt;        # If this is the same checksum as the last file then flag it.&lt;br /&gt;        if($1 =~ $lastmd5)&lt;br /&gt;        {&lt;br /&gt;                # If this is the first duplicate for this checksup then print the first file's name.&lt;br /&gt;                if(!$lastprint)&lt;br /&gt;                {&lt;br /&gt;                        print("$lastfile\n");&lt;br /&gt;                        $lastprint = 1;&lt;br /&gt;                }&lt;br /&gt;                # Print the conflicting file's name/&lt;br /&gt;                print("$newfile\n");&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;                $lastprint = 0;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        # Record the last filename and checksup for future testing.&lt;br /&gt;        $lastmd5 = $newmd5;&lt;br /&gt;        $lastfile = $newfile;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;close(FILE);&lt;br /&gt;&lt;br /&gt;# Remove the temp files.&lt;br /&gt;unlink($file);&lt;br /&gt;unlink($sort);&lt;br /&gt;&lt;/file&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1916381893164250464?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1916381893164250464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1916381893164250464&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1916381893164250464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1916381893164250464'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/02/find-duplicate-files-in-terminal.html' title='Find Duplicate Files in the Terminal'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3759169033958671393</id><published>2010-02-09T10:34:00.019-05:00</published><updated>2010-11-23T14:54:31.464-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Smarty'/><title type='text'>Switch Statment for Smarty 3</title><content type='html'>Here is the updated {switch} statement for Smarty 3.  The new version is NOT backwards compatible but the Smarty 2 version is still maintained &lt;a href="https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-2.0"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;11/23/2010 - Updated to version 3.5&lt;/span&gt;&lt;br /&gt;Updated to work with Smarty 3.0 release. (Tested on 3.0.5).&lt;br /&gt;I removed the code from this posting, it is now available on github here: &lt;a href="https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-3.0"&gt;https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-3.0&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;10/28/2010 - Updated to version 3.3&lt;/span&gt;&lt;br /&gt;I have added this project to GitHub at &lt;a href="http://github.com/pynej/Smarty-Switch-Statement"&gt;http://github.com/pynej/Smarty-Switch-Statement&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;02/25/2010 - Updated to version 3.3&lt;/span&gt;&lt;br /&gt;Please note that this update is required for version 3.0b6 or grater.  The change is simple renaming the execute methods to compile but it not backwards compatible.  The Smarty3.0b5 and below version is still available &lt;a href="http://pynej.dnsalias.com/Shared/sompile.switch.30b5.php.txt"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;02/09/2010 - Updated to version 3.2&lt;/span&gt;&lt;br /&gt;Fixed a bug when chaining case statements without a break.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;02/09/2010 - Updated to version 3.1&lt;/span&gt;&lt;br /&gt;Updated the plug-in to once again support the shorthand format, {switch $myvar}.  To enable this feature you must add the following line to you code somewhere before the template is executed.&lt;br /&gt;&lt;pre&gt;$smarty-&amp;gt;loadPlugin('smarty_compiler_switch');&lt;br /&gt;&lt;/pre&gt;If you do not add said line the long hand form will still work correctly.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;sample.php&lt;/span&gt;&lt;br /&gt;/**&lt;br /&gt;* Sample usage:&lt;br /&gt;* &amp;lt;code&amp;gt;&lt;br /&gt;* {foreach item=$debugItem from=$debugData}&lt;br /&gt;*  // Switch on $debugItem.type&lt;br /&gt;*    {switch $debugItem.type}&lt;br /&gt;*       {case 1}&lt;br /&gt;*       {case "invalid_field"}&lt;br /&gt;*          // Case checks for string and numbers.&lt;br /&gt;*       {/case}&lt;br /&gt;*       {case $postError}&lt;br /&gt;*       {case $getError|cat:"_ajax"|lower}&lt;br /&gt;*          // Case checks can also use variables and modifiers.&lt;br /&gt;*          {break}&lt;br /&gt;*       {default}&lt;br /&gt;*          // Default case is supported.&lt;br /&gt;*    {/switch}&lt;br /&gt;* {/foreach}&lt;br /&gt;* &amp;lt;/code&amp;gt;&lt;br /&gt;*&lt;br /&gt;* Note in the above example that the break statements work exactly as expected.  Also the switch and default&lt;br /&gt;*    tags can take the break attribute. If set they will break automatically before the next case is printed.&lt;br /&gt;*&lt;br /&gt;* Both blocks produce the same switch logic:&lt;br /&gt;* &amp;lt;code&amp;gt;&lt;br /&gt;*    {case 1 break}&lt;br /&gt;*       Code 1&lt;br /&gt;*    {case 2}&lt;br /&gt;*       Code 2&lt;br /&gt;*    {default break}&lt;br /&gt;*       Code 3&lt;br /&gt;* &amp;lt;/code&amp;gt;&lt;br /&gt;*&lt;br /&gt;* &amp;lt;code&amp;gt;&lt;br /&gt;*    {case 1}&lt;br /&gt;*     Code 1&lt;br /&gt;*       {break}&lt;br /&gt;*    {case 2}&lt;br /&gt;*       Code 2&lt;br /&gt;*    {default}&lt;br /&gt;*       Code 3&lt;br /&gt;*       {break}&lt;br /&gt;* &amp;lt;/code&amp;gt;&lt;br /&gt;*&lt;br /&gt;* Finally, there is an alternate long hand style for the switch statements that you may need to use in some cases.&lt;br /&gt;*&lt;br /&gt;* &amp;lt;code&amp;gt;&lt;br /&gt;* {switch var=$type}&lt;br /&gt;*    {case value="box" break=true}&lt;br /&gt;*    {case value="line"}&lt;br /&gt;*       {break}&lt;br /&gt;*    {default}&lt;br /&gt;* {/switch}&lt;br /&gt;* &amp;lt;/code&amp;gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3759169033958671393?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3759169033958671393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3759169033958671393&amp;isPopup=true' title='32 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3759169033958671393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3759169033958671393'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/02/switch-statment-for-smarty-3.html' title='Switch Statment for Smarty 3'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>32</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3869735754215281665</id><published>2010-02-08T14:47:00.003-05:00</published><updated>2010-02-08T15:00:06.078-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PDF'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenSSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Digital signature'/><title type='text'>Creating Acrobat Digital Signatures with a Root CA for Validation</title><content type='html'>Recently I was looking into using Adobe PDF Signing.  This feature requires that each user have a digital certificate for each user.  The problem is that creating the default signatures in Acrobat then every certificate to be imported on every other computer.  That is to set up 10 users to all properly authenticate signatures you would have to import 10 signatures onto 10 computers witch becomes prohibitively complex.&lt;br /&gt;&lt;br /&gt;There is another option.  If the users certificates are all signed with a single CA(Certificate Authority) then only the CA needs to get imported to get all the certificate validation working.  This is the approach I used but it is not internally supported by Acrobat and requires a Linux box to create the certificates.  This guide will show you how to create a CA and signed digital certificates for your users.  Then you simply import the single CA into each computer along with the actual users certificate.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Requirements:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;OpenSSL is required to do most of the work.&lt;/li&gt;&lt;li&gt;Acrobat Reader is all that is required on the user computers.&lt;/li&gt;&lt;li&gt;One copy of Acrobat Standard is needed to enable digital Digital Rights management on PDF files.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Creating the Certificate Authority:&lt;/span&gt;&lt;br /&gt;Run the following command to generate  new CA under the current directory.  You need to make sure this is in a secure path.&lt;br /&gt;&lt;pre&gt;/usr/share/ssl/misc/CA.pl -newca&lt;br /&gt;&lt;/pre&gt;The password prompt is the CA password and is needed by the administrator when signing new certificates.  The rest of the prompts create the CA identification and signature and can not be changed once set. Once finished the demoCA directory can be moved and renamed as necessary.&lt;br /&gt;&lt;br /&gt;Once done you need to edit the /etc/ssl/openssl.cnf configuration file and update the &lt;span style="font-style: italic;"&gt;CA_default.dir&lt;/span&gt; variable.&lt;br /&gt;&lt;pre&gt;[ CA_default ]&lt;br /&gt;dir    = /root/keys/CompanyCA&lt;/pre&gt;&lt;br /&gt;Create an &lt;span style="font-style: italic;"&gt;acrobat.cnf&lt;/span&gt; configuration for creating user certificates.&lt;br /&gt;&lt;pre&gt;echo keyUsage=digitalSignature, dataEncipherment &gt; acrobat.cnf&lt;br /&gt;echo 1.2.840.113583.1.1.10=DER:05:00 &gt;&gt; acrobat.cnf&lt;/pre&gt;&lt;br /&gt;Next you probably want to extend the CA expiration date beyond one year.  The following command will extend it to ten years.&lt;br /&gt;&lt;pre&gt;openssl x509 -in cacert.pem -days 3650 -signkey ./private/cakey.pem -out cacert.pem&lt;/pre&gt;&lt;br /&gt;Finally the &lt;span style="font-style: italic;"&gt;cacert.pem&lt;/span&gt; to a shared location  and rename it to end with a &lt;span style="font-style: italic;"&gt;.cer&lt;/span&gt; file extension so that the clients can import it.  This is the public CA certificate used for validating certificates.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Create a Users Digital Certificate:&lt;/span&gt;&lt;br /&gt;Create the new users certificate.  You will be prompted to enter the end users password that they will type to sign documents.&lt;br /&gt;&lt;pre&gt;/usr/share/ssl/misc/CA.pl -newcert&lt;/pre&gt;&lt;br /&gt;Now run the next command to sign the generated certificate with the CA.  You will be prompted for the CA password.&lt;br /&gt;&lt;pre&gt;openssl x509 -in newcert.pem -CA cacert.pem -CAkey private/cakey.pem -CAcreateserial -out newcert.pem -days 3650 -clrext -extfile acrobat.cnf&lt;/pre&gt;&lt;br /&gt;Finally run the following command to export this certificate as a PKCS12 package witch Acrobat can import.&lt;br /&gt;&lt;pre&gt;cat newkey.pem newcert.pem  | openssl pkcs12 -export &gt; username.pfx&lt;/pre&gt;&lt;br /&gt;You can now copy this file out to the same shared location at the CA.  It is password protected and the certificates can be extracted from it in the future so a backup of the generated new*.pem files is not needed.&lt;br /&gt;&lt;br /&gt;To extract the certificate and keys you can run the flowing commands.&lt;br /&gt;&lt;pre&gt;openssl pkcs12 -in username.pfx -nokeys -out newcert.pem&lt;br /&gt;openssl pkcs12 -in username.pfx -nocert -out newkey.pem&lt;span style="font-family: Georgia,serif;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Import the Certificate Authority and Users Digital Certificate:&lt;/span&gt;&lt;br /&gt;On any computers that need to be able to validate signatures all you need to do is import the CA file.  To do so simply open up Acrobat and go to &lt;span style="font-style: italic;"&gt;Document-&gt;Manage Trust Identities&lt;/span&gt;.  Then browse for the &lt;span style="font-style: italic;"&gt;*.cer&lt;/span&gt; CA file and import it. After importing you need to select the certificate, select &lt;span style="font-style: italic;"&gt;Trust&lt;/span&gt;, and check the &lt;span style="font-style: italic;"&gt;Use this Certificate as a Trusted Root&lt;/span&gt; option.&lt;br /&gt;&lt;br /&gt;To enable a user to sign documents on a computers you need to do the following steps.  Open up Acrobat and go to &lt;span style="font-style: italic;"&gt;Document-&gt;Security Settings&lt;/span&gt;.  Then click&lt;span style="font-style: italic;"&gt; Add ID&lt;/span&gt; and browse for the proper users &lt;span style="font-style: italic;"&gt;*.pfx&lt;/span&gt; file.  You will need to enter the users password once to install the certificate but users will still need to enter the password when signing documents.  These certificates are still password protected so multiple signatures can be  loaded onto the same computer without issue.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3869735754215281665?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3869735754215281665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3869735754215281665&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3869735754215281665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3869735754215281665'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/02/creating-acrobat-digital-signatures.html' title='Creating Acrobat Digital Signatures with a Root CA for Validation'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1541056310647271796</id><published>2010-01-29T11:25:00.005-05:00</published><updated>2010-02-10T15:58:45.523-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PDF'/><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Printing'/><title type='text'>Print to PDF from the Linux Terminal</title><content type='html'>A while back I had to set up a system for printing reports to PDF files automatically.  IE: I needed a script to do the conversion, retrieve the new file, fix the orientation, and return the new filename.  Here is the script and the details and the requirements.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Prerequisites:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://http//www.physik.uni-wuerzburg.de/%7Evrbehr/cups-pdf/"&gt;CUPS-PDF&lt;/a&gt;: This package provides a PDF printer that we can print to using lp.&lt;/li&gt;&lt;li&gt;&lt;a href="http://http//www.tug.org/texlive/"&gt;TexLive&lt;/a&gt;: This is a large project with many tools and has a large footprint but it is necessary for possessing landscape jobs.  The exact problem is that  jobs printed in landscape will be miss oriented when viewed and this allows us to correct that problem.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://http//www2.warwick.ac.uk/fac/sci/statistics/staff/academic/firth/software/pdfjam/"&gt;pdfjam&lt;/a&gt;: The project listed but does not need to be installed, rather a custom version of the pdf90 script included in this project is needed.  Specifically the script is customized to rotate the page counter-clockwise.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Files:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://pynej.dnsalias.com/Shared/printpdf.pl"&gt;printpdf&lt;/a&gt;: This is the main script and can be called from any external tools.&lt;/li&gt;&lt;li&gt;&lt;a href="http://pynej.dnsalias.com/Shared/pdf90minus"&gt;pdf90minus&lt;/a&gt;: This is the modified version of pdf90 from pdfjam that rotates the pages of a PDF counter-clockwise.&lt;/li&gt;&lt;li&gt;&lt;a href="http://pynej.dnsalias.com/Shared/texlive.profile"&gt;texlive.profile&lt;/a&gt;: This is a installer configuration for TexLive with just the necessary components selected.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Configuration:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Install CUPS-PDF and configure a printer for it.  To configure the printer you can use the CUPS web interface or add the following lines manually.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;/etc/cups/printers.conf&lt;/span&gt;&lt;br /&gt;&amp;lt;printer pdf=""&amp;gt;&lt;br /&gt;Info PDF Writer for CUPS&lt;br /&gt;Location PDF Backend /usr/lib64/cups/backend/pdf-writer&lt;br /&gt;DeviceURI pdf-writer:/tmp/&lt;br /&gt;State Idle&lt;br /&gt;Accepting Yes&lt;br /&gt;JobSheets none none&lt;br /&gt;QuotaPeriod 0&lt;br /&gt;PageLimit 0&lt;br /&gt;KLimit 0&lt;br /&gt;&amp;lt;/printer&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Install TexLive.  To install the minimal required components should download the &lt;a href="http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz"&gt;installer&lt;/a&gt; and the profile and extract them to the same location.  then run the following command from that folder.&lt;br /&gt;&lt;pre&gt;sudo ./install-tl −profile texlive.profile&lt;/pre&gt;&lt;br /&gt;Save the pdf90minus and pdfprint scripts to the same location.  You may wish to customize the working path /tmp but it must be the same in the printer configuration and printpdf script.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Usage:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can now test the PDF printing, the job will print and the PDF will be generated at the same path with the same name as teh original file only suffixed with .pdf.&lt;br /&gt;&lt;pre&gt;./printpdf myfile.txt [--rotate|-r] [--save|-s]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The --rotate option causes the job to be printed in landscape mode and then the PDF pages to be reoriented to display properly.&lt;br /&gt;The --save option causes the source file to be left behind and not deleted once successful printed.&lt;br /&gt;The script will block execution until the print job has finished and will then return passing the new filename to STDOUT.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1541056310647271796?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1541056310647271796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1541056310647271796&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1541056310647271796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1541056310647271796'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/01/print-to-pdf-from-linux-terminal.html' title='Print to PDF from the Linux Terminal'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-8102537034070428387</id><published>2010-01-29T11:13:00.003-05:00</published><updated>2011-07-21T17:33:02.731-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Automator'/><title type='text'>Auto Mount NFS Shares via Bonjour</title><content type='html'>&lt;div style="margin-bottom: 0in;"&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;07/21/2011 - Update&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It papers this site is no longer available. &amp;nbsp;As such I have posted the original script &lt;a href="http://pynej.dnsalias.com/Shared/bonjournfsmd.zip"&gt;here&lt;/a&gt;&amp;nbsp;and the automated workflow is&amp;nbsp;&amp;nbsp;&lt;a href="http://pynej.dnsalias.com/Shared/Automount%20Bonjour%20Shares.zip"&gt;here&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;There are also installation instructions for OSX 10.7 which is working but requires some extra setup and &lt;a href="http://itunes.apple.com/us/app/xcode/id448457090?mt=12"&gt;Xcode&lt;/a&gt; must also be installed before the scripts.&lt;br /&gt;&lt;br /&gt;Installation:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download the two file above and extract them someplace.&lt;/li&gt;&lt;li&gt;Drag the Automator action into our Applications folder and add it to your user accounts Login Items list so that it will automatically run each login.&lt;/li&gt;&lt;li&gt;Setup the background script to do the actual work.&lt;/li&gt;&lt;/ul&gt;Open up the Terminal application and locate the directory you saved the script into.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;cd ~/Downloads&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Run the following commands to install the script locally.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo mkdir -p /usr/local/bin&lt;br /&gt;sudo cp&amp;nbsp;bonjournfsmd.rb /usr/local/bin/bonjournfsmd.rb&lt;br /&gt;sudo chown root:wheel /usr/local/bin/bonjournfsmd.rb&lt;br /&gt;sudo chmod +x /usr/local/bin/bonjournfsmd.rb&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Run the following commands to install some dependancies required on OSX 10.7 only.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo gem install dnssd&lt;br /&gt;sudo gem install daemons&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr /&gt;&lt;div style="margin-bottom: 0in;"&gt;A wile back I switched my server over to FreeBSD/ZFS and ended up changing the shared volumes to use the NFS features of ZFS.   &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;There were a few minor issues with the initial configuration and the OSX clients but this was not to difficult to resolve.  Specifically the NFS system does not allow for user account mapping, but rather the accounts on both the server and client must be identical.  Furthermore this mapping is not done by username, but rather by the UID of each user.  On OSX the UID start at 501 where as on FreeBSD they default to 1000.  The best approach to fix this problem is simply to update the account on the BSD box to use the same UID as OSX.  (It is possible to change UID in OSX but not recommended.)  After this fix everything was working correctly including write access and ACL lists.   &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;The other thing I wanted to do, and the reason for this post, was to have my OSX desktop automatically map these volumes on boot.  I found a simple  Bonjour NFS share Mounter Daemon, &lt;a href="http://svwtech.com/site"&gt;http://svwtech.com/site&lt;/a&gt;, that can be run from the terminal and will do exactly that but I was unable to get the script to run at boot.  In the end I had to create a Automator application to run the script and then add that application to my startup items.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;You can download the Application with the above auto-mount script embedded in it &lt;a href="http://pynej.dnsalias.com/Shared/Automount%20Bonjour%20Shares.zip"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-8102537034070428387?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/8102537034070428387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=8102537034070428387&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8102537034070428387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8102537034070428387'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/01/auto-mount-nfs-shares-via-bonjour.html' title='Auto Mount NFS Shares via Bonjour'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2273511476158806296</id><published>2010-01-29T09:52:00.004-05:00</published><updated>2011-03-04T08:23:42.603-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Automator'/><title type='text'>Find Duplicate Photos in an iPhoto Library</title><content type='html'>&lt;b&gt;03/04/2011- Update: &lt;/b&gt; Updated the service with some fixes for invalid paths and quoting problems.&lt;br /&gt;&lt;br /&gt;There are many shareware tools for finding duplicate pictures in an iPhoto library but this should be a simple operation, and honestly shouldn't require a fee to utilize.  As a result I created a simple Service via Automator to solve this problem. &lt;br /&gt;&lt;br /&gt;This service is very straightforward, simply select your iPhoto library in finder and go to Services-&gt;Find Duplicates in iPhoto.&lt;br /&gt;&lt;br /&gt;The service will do the following steps:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Calculate the md5 of each original picture in the library.  This happens to the original backup of the picture so an edited version will still get caught as a duplicate. &lt;/li&gt;&lt;li&gt;Sort the list and find any pictures that have the same checksum.&lt;/li&gt;&lt;li&gt;Add any duplicates found to a new Album named Duplicates. (I had intended to use a keyword but this turned out to be simpler.)&lt;/li&gt;&lt;/ul&gt;You can then view the Album and flag and delete photos at your discretion.&lt;br /&gt;&lt;br /&gt;Notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;No files are actually deleted, just flagged.&lt;/li&gt;&lt;li&gt;The way iPhoto works choosing delete will just remove the photo from he album, not from iPhoto.  To get around this you can select the photos from the album then switch to the all Pictures view.  Your selection will remain and you can then choose to delete the selected photos.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Install Instructions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download the service: &lt;a href="http://pynej.dnsalias.com/Shared/Find%20Duplicates%20in%20iPhoto.zip"&gt;Find Duplicates in iPhoto.zip&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Extract the service into the '~/Library/Services/' or '/Library/Services/' folder.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2273511476158806296?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2273511476158806296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2273511476158806296&amp;isPopup=true' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2273511476158806296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2273511476158806296'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2010/01/find-duplicate-photos-in-iphoto-library.html' title='Find Duplicate Photos in an iPhoto Library'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-5201971801614587893</id><published>2009-10-08T15:49:00.002-04:00</published><updated>2009-10-08T16:47:10.350-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Filesystem Based Events with Incron</title><content type='html'>Ok, so I installed and configured &lt;a href="http://inotify.aiken.cz/?section=incron&amp;amp;page=about&amp;amp;lang=en"&gt;incron&lt;/a&gt; witch is a nice cron like system for subscribing to file system events such as file creation or modification.  It works great with jobs like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;/mydir/processing IN_CLOSE_WRITE mv $@/$# $@/../done&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This line will look for new files written to /mydir/processing/ and move them to /mydir/done when they are finished witting and closed. (Won't work for log-type files that are kept open but there are other options for that.) &lt;br /&gt;&lt;br /&gt;However you cant do either of the following things:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;/mydir/processing/file*.txt IN_CLOSE_WRITE mv $@/$# $@/../done&lt;br /&gt;/mydir/processing IN_CLOSE_WRITE cd $@ &amp;&amp; ../proc_file $# &amp;&amp; rm $#&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The first example tried to monitor only a subset of the directory witch doesn't work. (Though you can monitor a single file by name.) The second example tries to execute a series bash operations on the file witch doesn't work.  (Only one command can be executed, with no popping support.)&lt;br /&gt;&lt;br /&gt;Both of these problems can be solved by writing a external bash file and calling that instead and passing it the file name like so.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;/mydir/processing IN_CLOSE_WRITE myscript $#&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But alas I'd prefer to not have hundreds of bash scripts and have the same capabilities for piping and concatenation that cron has.  As it tunes out, after a few hours of screwing with it, two simple shell scripts will solve both these problems.  With those two scripts installed the follwing command will work.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;/mydir/processing IN_CLOSE_WRITE run filter $# ^*txt$ &amp;&amp; cd $@ &amp;&amp; ../proc_file $# &amp;&amp; rm $#&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;In this case the run option will pass the rest of the line on to bash for full fledged execution.  As the first step in the execution it will run the filter operation witch will return true if the filename matches the regex sting and continue execution or return false if not and end the command.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;/usr/bin/run&lt;/span&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;eval $@&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;/usr/bin/filter&lt;/span&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;if [[ "$1" =~ $2 ]]; then &lt;br /&gt;    exit 0;&lt;br /&gt;else&lt;br /&gt;    exit 1;&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-5201971801614587893?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/5201971801614587893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=5201971801614587893&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5201971801614587893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5201971801614587893'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/10/filesystem-based-events-with-incron.html' title='Filesystem Based Events with Incron'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-259410836221557906</id><published>2009-10-01T14:28:00.003-04:00</published><updated>2009-10-01T14:34:13.184-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Themes'/><category scheme='http://www.blogger.com/atom/ns#' term='Firefox'/><title type='text'>Gmail Themes for Firefox</title><content type='html'>I have used these themes before but now theres a single add-on for all 3:  &lt;br /&gt;&lt;br /&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/8434?collection_uuid=c194cfdd%26%2345%3B3be7%26%2345%3B5724%26%2345%3B27bf%26%2345%3Bd5571e12aa30"&gt;https://addons.mozilla.org/en-US/firefox/addon/8434?collection_uuid=c194cfdd%26%2345%3B3be7%26%2345%3B5724%26%2345%3B27bf%26%2345%3Bd5571e12aa30a&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-259410836221557906?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/259410836221557906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=259410836221557906&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/259410836221557906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/259410836221557906'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/10/gmail-themes-for-firefox.html' title='Gmail Themes for Firefox'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6885981060248147896</id><published>2009-07-02T16:50:00.001-04:00</published><updated>2009-07-02T16:52:21.685-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Firefox'/><title type='text'>Installing Firefox 3.5 on Ubuntu</title><content type='html'>Just a quick note.  If you noticed the new Firefox 3.5 release and want it in Ubuntu just run the following command.&lt;br /&gt;&lt;pre&gt;sudo apt-get install firefox-3.5&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6885981060248147896?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6885981060248147896/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6885981060248147896&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6885981060248147896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6885981060248147896'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/07/installing-firefox-35-on-ubuntu.html' title='Installing Firefox 3.5 on Ubuntu'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4248821689882533116</id><published>2009-04-28T16:20:00.002-04:00</published><updated>2009-04-28T16:24:11.488-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Enable PulseAudio After Upgrading to Ubuntu Jaunty</title><content type='html'>Just a note on a minor problem when I was upgrading my server to Jaunty.  As it was an upgrade PaulseAudio was not installed for me.  To manually install it add the pavucontrol package, or run the following command.&lt;br /&gt;&lt;pre&gt;sudo apt-get install pavucontrol&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4248821689882533116?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4248821689882533116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4248821689882533116&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4248821689882533116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4248821689882533116'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/04/enable-pulseaudio-after-upgrading-to.html' title='Enable PulseAudio After Upgrading to Ubuntu Jaunty'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2277773707550653076</id><published>2009-03-10T13:53:00.002-04:00</published><updated>2009-03-10T14:28:16.150-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Regex'/><title type='text'>Reading CSV file in Regex</title><content type='html'>Csv filws come in many shapes and forms, and its trivial to read most a csv files.  But then again it can be quite headache when the csv file has a mixture of formats and control characters.&lt;br /&gt;&lt;br /&gt;The simplest way to parse a csv file it to use a simple regexp.  One might start with something as simple as this:&lt;br /&gt;&lt;pre&gt;split(/,/, $line);&lt;br /&gt;&lt;/pre&gt;But of course this is to simple.  It will split on every coma and thus not work for a line like this:&lt;br /&gt;&lt;pre&gt;123,asd,"123, asd"&lt;br /&gt;&lt;/pre&gt;Similarly the following will work:&lt;br /&gt;&lt;pre&gt;split(/","/, $line);&lt;br /&gt;"123","asd","123, asd"&lt;br /&gt;&lt;/pre&gt;But what if we don't have control over the quoter and a mixture of both is generated?  And furthermore what if   the fields can contain quotes and comas themselves?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In my case I have to parse a file that is quoted as such:&lt;br /&gt;&lt;pre&gt;005101,"LITERATURE-P/S, WARRANTY","4,345,211.0000",0.0292"70P"&lt;br /&gt;&lt;/pre&gt;To phase this the regexp is a bit more complicated:&lt;br /&gt;&lt;pre&gt;@line = split(/,(?!(?:[^",]|[^"],[^"])+")/, $line);&lt;br /&gt;for $item (@line)&lt;br /&gt;{&lt;br /&gt;  $item[$c] =~ s/^"(.*)"$/$1/;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;In this case we first split the string on any coma but look ahead each time to check and see if the current coma is part of a quoted literal.  If that is the case then the coma is skipped over.  Next we look through each item and remove the quotation's if they are present.&lt;br /&gt;&lt;br /&gt;This previous example is almost perfect but there is one more problem.  Try the next item and it will parse wrong.&lt;br /&gt;&lt;pre&gt;005101,"LITERATURE-P/S WARRANTY,","4,345,211.0000",0.0292,"70P"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note the extra coma at the very end of the literals.  This trips up the regexp logic.  The simplest solution I found to this problem was to just add a extra space in this case and then trim it at the end.&lt;br /&gt;&lt;pre&gt;$line =~ s/,",/, ",/g;&lt;br /&gt;@line = split(/,(?!(?:[^",]|[^"],[^"])+")/, $line);&lt;br /&gt;for $item (@line)&lt;br /&gt;{&lt;br /&gt;  $line[$c] =~ s/, $/,/;&lt;br /&gt;  $item[$c] =~ s/^"(.*)"$/$1/;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The final script can be found below.  The script included does a bit more then just phase a csv, it writes it back out as a fixed width file.  This can be easily changed to just store the data in an array.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;csvtofixed.pl&lt;/span&gt;&lt;br /&gt;#!/usr/bin/perl&lt;br /&gt;&lt;br /&gt;# Input File.&lt;br /&gt;$in = shift;&lt;br /&gt;# Output File.&lt;br /&gt;$out = shift;&lt;br /&gt;# Fixed width padding.&lt;br /&gt;@size = split(/,/, shift);&lt;br /&gt;&lt;br /&gt;if(!$in || !$out || $#size == -1)&lt;br /&gt;{&lt;br /&gt;  print "usage: csvtofixed input output columns\n";&lt;br /&gt;  print "\tinput: Filename to read in.\n";&lt;br /&gt;  print "\toutput: Filename to write out to, will overwrite.\n";&lt;br /&gt;  print "\tcoumns: Field widths to pad input fields to. Example: 20,15,3,10\n";&lt;br /&gt;  exit;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if($in =~ $out)&lt;br /&gt;{&lt;br /&gt;print "Aborted: Can't use same input and output file.  Please use a temparay $&lt;br /&gt;exit;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# Open Files.&lt;br /&gt;open(IN, $in) or die "Can't open input file $in";&lt;br /&gt;open(OUT, "&gt;$out") or die "Can't create output file $out";&lt;br /&gt;&lt;br /&gt;# While there is input.&lt;br /&gt;while(&amp;th;in&amp;gt;)&lt;br /&gt;{&lt;br /&gt;# Read the next line.&lt;br /&gt;$line = $_;&lt;br /&gt;&lt;br /&gt;# Trim off the end.&lt;br /&gt;$line =~ s/\r\n//;&lt;br /&gt;# Fix for coma as the last char in a quote bug.&lt;br /&gt;$line =~ s/,",/, ",/g;&lt;br /&gt;&lt;br /&gt;# Split the line into its parts.&lt;br /&gt;@line = split(/,(?!(?:[^",]|[^"],[^"])+")/, $line);&lt;br /&gt;&lt;br /&gt;# For each column.&lt;br /&gt;for($c=0;$c&amp;lt;=$#size;$c++)&lt;br /&gt;{&lt;br /&gt; # Trim and quoted fields.&lt;br /&gt; $line[$c] =~ s/^"(.*)"$/$1/;&lt;br /&gt; # Remove the extra space for the coma fix.&lt;br /&gt; $line[$c] =~ s/, $/,/;&lt;br /&gt;&lt;br /&gt; # Print out the field.&lt;br /&gt; printf OUT "%*.*s|", $size[$c], $size[$c], $line[$c];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# Finish the line.&lt;br /&gt;print OUT "\r\n";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# Close the files.&lt;br /&gt;close(IN);&lt;br /&gt;close(OUT);&lt;br /&gt;&lt;br /&gt;exit;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2277773707550653076?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2277773707550653076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2277773707550653076&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2277773707550653076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2277773707550653076'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/03/reading-csv-file-in-regex.html' title='Reading CSV file in Regex'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2813010811549257862</id><published>2009-02-27T13:27:00.011-05:00</published><updated>2009-07-02T16:48:33.003-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex Date and Time Stepper Components</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Update- 7/02/09 4:48 PM&lt;/span&gt;&lt;br /&gt;Added &lt;a href="http://pynej.dnsalias.com/Web/DateTimeStepper/"&gt;Sample&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update- 4/28/09 4:14 PM&lt;/span&gt;&lt;br /&gt;Fixed a problem with the stepper stepping by year.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update- 3/20/09 2:52 PM&lt;/span&gt;&lt;br /&gt;Added the missing CustomStepperEvent class.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update- 3/9/09 9:44 PM&lt;/span&gt;&lt;br /&gt;Fixed a bug with a stepType of month and years.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;So after fiddling with trying to make simple easy to use date and time controls smiler to the internal NumberStepper I was never able to fully replicate the NumberStepper interface, nor extend it.  As  a last attempt I duplicated the entire NUmberStepper class and revised it.  Thus I bring you the CustomStepper class.  This is a reimplementation of the NumberStepper that uses a Date instead of a Number as the value.  It can also display said date in any format and it can step my any date component.&lt;br /&gt;&lt;br /&gt;There are two extra classes, the DateStepper and TimeStepper, as well for simplified code.  The DateStepper defaults to stepping one day at a time and renders in the MM/DD/YYYY format while the TimeStepper defaults to stepping one hour at a time and renders in the HH:MM AM|PM format.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;Sample:&lt;/span&gt;&lt;br /&gt;&amp;lt;hines:DateStepper id="begdate" value="{DateUtil.now.assign(DateUtil.DAY, 0).date}" stepSize="7"/&amp;gt;&lt;br /&gt;&amp;lt;hines:TimeStepper id="begdate" value="{DateUtil.now.assign(DateUtil.DAY, 0).date}" stepType="minutes" stepSize="15"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Code:&lt;/span&gt;&lt;br /&gt;The following style sheet needs to be included in your main application or the code withing put into an existing style sheet.&lt;br /&gt;&lt;pre&gt;&amp;lt;Application&amp;gt;&lt;br /&gt; &amp;lt;mx:Style source="style/CustomStepperStyle.css"/&amp;gt;&lt;br /&gt;&amp;lt;/Application&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;CustomStepperStyle.css&lt;/span&gt;&lt;br /&gt;CustomStepper&lt;br /&gt;{&lt;br /&gt;cornerRadius: 5;&lt;br /&gt;downArrowDisabledSkin: ClassReference("mx.skins.halo.NumericStepperDownSkin");&lt;br /&gt;downArrowDownSkin: ClassReference("mx.skins.halo.NumericStepperDownSkin");&lt;br /&gt;downArrowOverSkin: ClassReference("mx.skins.halo.NumericStepperDownSkin");&lt;br /&gt;downArrowUpSkin: ClassReference("mx.skins.halo.NumericStepperDownSkin");&lt;br /&gt;focusRoundedCorners: "tr br"; /* Only round the right corners of the focus rect */&lt;br /&gt;upArrowDisabledSkin: ClassReference("mx.skins.halo.NumericStepperUpSkin");&lt;br /&gt;upArrowDownSkin: ClassReference("mx.skins.halo.NumericStepperUpSkin");&lt;br /&gt;upArrowOverSkin: ClassReference("mx.skins.halo.NumericStepperUpSkin");&lt;br /&gt;upArrowUpSkin: ClassReference("mx.skins.halo.NumericStepperUpSkin");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;These are the two helper classes.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;DateStepper.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt;/**&lt;br /&gt; * This is a custom DateStepper class based off the CustomStepper class and using the preconfigured date style.&lt;br /&gt; *&lt;br /&gt; * If fullButtons is set then larger buttons on the left and right sides will be used instead of buttons split vertically. &lt;br /&gt; * This is mainly used for the touch screens to be more accessible.&lt;br /&gt; */&lt;br /&gt;public class DateStepper extends CustomStepper&lt;br /&gt;{&lt;br /&gt; /**&lt;br /&gt;  * Create a new instance of DateStepper.&lt;br /&gt;  */&lt;br /&gt; public function DateStepper()&lt;br /&gt; {&lt;br /&gt;  super(CustomStepper.TYPEDATE);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;TimeStepper.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt;/**&lt;br /&gt; * This is a custom TimeStepper class based off the CustomStepper class and using the preconfigured time style.&lt;br /&gt; */&lt;br /&gt;public class TimeStepper extends CustomStepper&lt;br /&gt;{&lt;br /&gt; /**&lt;br /&gt;  * Create a new instance of TimeStepper.&lt;br /&gt;  */&lt;br /&gt; public function TimeStepper()&lt;br /&gt; {&lt;br /&gt;  super(CustomStepper.TYPETIME);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A custom is also needed for change events.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;CustomStepperEvent.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt;import flash.events.Event;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; *  Represents events that are specific to the NumericStepper control.&lt;br /&gt; *&lt;br /&gt; *  @see mx.controls.NumericStepper&lt;br /&gt; */&lt;br /&gt;public class CustomStepperEvent extends Event&lt;br /&gt;{&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt; //&lt;br /&gt; //  Class constants&lt;br /&gt; //&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  *  The &amp;lt;code&amp;gt;NumericStepperEvent.CHANGE&amp;lt;/code&amp;gt; constant defines the value of the&lt;br /&gt;  *  &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; property of the event object for a &amp;lt;code&amp;gt;change&amp;lt;/code&amp;gt; event.&lt;br /&gt;  *&lt;br /&gt;  *&lt;br /&gt;  *  @eventType change&lt;br /&gt;  */&lt;br /&gt; public static const CHANGE:String = "change";&lt;br /&gt;&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt; //&lt;br /&gt; //  Constructor&lt;br /&gt; //&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  *  Constructor.&lt;br /&gt;  *&lt;br /&gt;  *  @param type The event type; indicates the action that caused the event.&lt;br /&gt;  *&lt;br /&gt;  *  @param bubbles Specifies whether the event can bubble up the display list hierarchy.&lt;br /&gt;  *&lt;br /&gt;  *  @param cancelable Specifies whether the behavior associated with the event can be prevented.&lt;br /&gt;  *&lt;br /&gt;  *  @param value The value of the NumericStepper control when the event was dispatched.&lt;br /&gt;         *&lt;br /&gt;         *  @param triggerEvent If the value changed in response to a user action, contains a value&lt;br /&gt;         *  indicating the type of input action, either &amp;lt;code&amp;gt;InteractionInputType.MOUSE&amp;lt;/code&amp;gt;&lt;br /&gt;         *  or &amp;lt;code&amp;gt;InteractionInputType.KEYBOARD&amp;lt;/code&amp;gt;.&lt;br /&gt;  */&lt;br /&gt; public function CustomStepperEvent(type:String, bubbles:Boolean = false,&lt;br /&gt;                                        cancelable:Boolean = false,&lt;br /&gt;                                        value:Date = null,&lt;br /&gt;                                        triggerEvent:Event = null)&lt;br /&gt; {&lt;br /&gt;  super(type, bubbles, cancelable);&lt;br /&gt;&lt;br /&gt;        this.value = value;&lt;br /&gt;        this.triggerEvent = triggerEvent;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt; //&lt;br /&gt; //  Properties&lt;br /&gt; //&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt; //----------------------------------&lt;br /&gt; //  value&lt;br /&gt; //----------------------------------&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * The value of the NumericStepper control when the event was dispatched.&lt;br /&gt;  */ &lt;br /&gt; public var value:Date;&lt;br /&gt;&lt;br /&gt; //----------------------------------&lt;br /&gt; //  triggerEvent&lt;br /&gt; //----------------------------------&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  *  If the value is changed in response to a user action, &lt;br /&gt;  *  this property contains a value indicating the type of input action. &lt;br /&gt;  *  The value is either &amp;lt;code&amp;gt;InteractionInputType.MOUSE&amp;lt;/code&amp;gt; &lt;br /&gt;  *  or &amp;lt;code&amp;gt;InteractionInputType.KEYBOARD&amp;lt;/code&amp;gt;.&lt;br /&gt;  */&lt;br /&gt; public var triggerEvent:Event;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt; //&lt;br /&gt; //  Overridden methods: Event&lt;br /&gt; //&lt;br /&gt; //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  *  @private&lt;br /&gt;  */&lt;br /&gt; override public function clone():Event&lt;br /&gt; {&lt;br /&gt;  return new CustomStepperEvent(type, bubbles, cancelable, value);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Main code of the CustomStepper class.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;CustomStepper.as&lt;/span&gt;&lt;br /&gt;////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;//&lt;br /&gt;//  ADOBE SYSTEMS INCORPORATED&lt;br /&gt;//  Copyright 2003-2007 Adobe Systems Incorporated&lt;br /&gt;//  All Rights Reserved.&lt;br /&gt;//&lt;br /&gt;//  NOTICE: Adobe permits you to use, modify, and distribute this file&lt;br /&gt;//  in accordance with the terms of the license agreement accompanying it.&lt;br /&gt;//&lt;br /&gt;//  Updated by Jeremy Pyne &amp;lt;jeremy pyne at gmail dot com&amp;gt;&lt;br /&gt;//    Changed stepper value to a Date for use in Date and Time steppers.&lt;br /&gt;////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;import flash.display.DisplayObject;&lt;br /&gt;import flash.events.Event;&lt;br /&gt;import flash.events.FocusEvent;&lt;br /&gt;import flash.events.KeyboardEvent;&lt;br /&gt;import flash.events.MouseEvent;&lt;br /&gt;import flash.text.TextField;&lt;br /&gt;import flash.text.TextLineMetrics;&lt;br /&gt;import flash.ui.Keyboard;&lt;br /&gt;&lt;br /&gt;import mx.controls.listClasses.BaseListData;&lt;br /&gt;import mx.controls.listClasses.IDropInListItemRenderer;&lt;br /&gt;import mx.controls.listClasses.IListItemRenderer;&lt;br /&gt;import mx.core.FlexVersion;&lt;br /&gt;import mx.core.IDataRenderer;&lt;br /&gt;import mx.core.IIMESupport;&lt;br /&gt;import mx.core.UIComponent;&lt;br /&gt;import mx.core.UITextField;&lt;br /&gt;import mx.core.mx_internal;&lt;br /&gt;import mx.events.FlexEvent;&lt;br /&gt;import mx.formatters.DateFormatter;&lt;br /&gt;import mx.managers.IFocusManager;&lt;br /&gt;import mx.managers.IFocusManagerComponent;&lt;br /&gt;import mx.styles.StyleProxy;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;use namespace mx_internal;&lt;br /&gt;&lt;br /&gt;//--------------------------------------&lt;br /&gt;//  Events&lt;br /&gt;//--------------------------------------&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Dispatched when the value of the NumberStepper control changes&lt;br /&gt;*  as a result of user interaction.&lt;br /&gt;*&lt;br /&gt;*  @eventType mx.events.CustomStepperEvent.CHANGE&lt;br /&gt;*/&lt;br /&gt;[Event(name="change", type="hines.CustomStepperEvent")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Dispatched when the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; property changes.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;When you use a component as an item renderer,&lt;br /&gt;*  the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; property contains the data to display.&lt;br /&gt;*  You can listen for this event and update the component&lt;br /&gt;*  when the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; property changes.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  @eventType mx.events.FlexEvent.DATA_CHANGE&lt;br /&gt;*/&lt;br /&gt;[Event(name="dataChange", type="mx.events.FlexEvent")]&lt;br /&gt;&lt;br /&gt;//--------------------------------------&lt;br /&gt;//  Styles&lt;br /&gt;//--------------------------------------&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Color of text in the component, including the component label.&lt;br /&gt;*&lt;br /&gt;*  @default 0x0B333C&lt;br /&gt;*/&lt;br /&gt;[Style(name="color", type="uint", format="Color", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Color of text in the component if it is disabled.&lt;br /&gt;*&lt;br /&gt;*  @default 0xAAB3B3&lt;br /&gt;*/&lt;br /&gt;[Style(name="disabledColor", type="uint", format="Color", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Sets the &amp;lt;code&amp;gt;antiAliasType&amp;lt;/code&amp;gt; property of internal TextFields. The possible values are&lt;br /&gt;*  &amp;lt;code&amp;gt;"normal"&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;flash.text.AntiAliasType.NORMAL&amp;lt;/code&amp;gt;)&lt;br /&gt;*  and &amp;lt;code&amp;gt;"advanced"&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;flash.text.AntiAliasType.ADVANCED&amp;lt;/code&amp;gt;).&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;The default value is &amp;lt;code&amp;gt;"advanced"&amp;lt;/code&amp;gt;, which enables advanced anti-aliasing for the font.&lt;br /&gt;*  Set to &amp;lt;code&amp;gt;"normal"&amp;lt;/code&amp;gt; to disable the advanced anti-aliasing.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This style has no effect for system fonts.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This style applies to all the text in a TextField subcontrol;&lt;br /&gt;*  you cannot apply it to some characters and not others.&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;*  @default "advanced"&lt;br /&gt;*&lt;br /&gt;*  @see flash.text.TextField&lt;br /&gt;*  @see flash.text.AntiAliasType&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontAntiAliasType", type="String", enumeration="normal,advanced", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the font to use.&lt;br /&gt;*  Unlike in a full CSS implementation,&lt;br /&gt;*  comma-separated lists are not supported.&lt;br /&gt;*  You can use any font family name.&lt;br /&gt;*  If you specify a generic font name,&lt;br /&gt;*  it is converted to an appropriate device font.&lt;br /&gt;*&lt;br /&gt;*  @default "Verdana"&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontFamily", type="String", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Sets the &amp;lt;code&amp;gt;gridFitType&amp;lt;/code&amp;gt; property of internal TextFields that represent text in Flex controls.&lt;br /&gt;*  The possible values are &amp;lt;code&amp;gt;"none"&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;flash.text.GridFitType.NONE&amp;lt;/code&amp;gt;),&lt;br /&gt;*  &amp;lt;code&amp;gt;"pixel"&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;flash.text.GridFitType.PIXEL&amp;lt;/code&amp;gt;),&lt;br /&gt;*  and &amp;lt;code&amp;gt;"subpixel"&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;flash.text.GridFitType.SUBPIXEL&amp;lt;/code&amp;gt;).&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This property only applies when you are using an embedded font&lt;br /&gt;*  and the &amp;lt;code&amp;gt;fontAntiAliasType&amp;lt;/code&amp;gt; property&lt;br /&gt;*  is set to &amp;lt;code&amp;gt;"advanced"&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This style has no effect for system fonts.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;This style applies to all the text in a TextField subcontrol;&lt;br /&gt;*  you can't apply it to some characters and not others.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  @default "pixel"&lt;br /&gt;* &lt;br /&gt;*  @see flash.text.TextField&lt;br /&gt;*  @see flash.text.GridFitType&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontGridFitType", type="String", enumeration="none,pixel,subpixel", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Sets the &amp;lt;code&amp;gt;sharpness&amp;lt;/code&amp;gt; property of internal TextFields that represent text in Flex controls.&lt;br /&gt;*  This property specifies the sharpness of the glyph edges. The possible values are Numbers&lt;br /&gt;*  from -400 through 400.&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This property only applies when you are using an embedded font&lt;br /&gt;*  and the &amp;lt;code&amp;gt;fontAntiAliasType&amp;lt;/code&amp;gt; property&lt;br /&gt;*  is set to &amp;lt;code&amp;gt;"advanced"&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This style has no effect for system fonts.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;This style applies to all the text in a TextField subcontrol;&lt;br /&gt;*  you can't apply it to some characters and not others.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  @default 0&lt;br /&gt;* &lt;br /&gt;*  @see flash.text.TextField&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontSharpness", type="Number", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Height of the text, in pixels.&lt;br /&gt;*&lt;br /&gt;*  The default value is 10 for all controls except the ColorPicker control.&lt;br /&gt;*  For the ColorPicker control, the default value is 11.&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontSize", type="Number", format="Length", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Determines whether the text is italic font.&lt;br /&gt;*  Recognized values are &amp;lt;code&amp;gt;"normal"&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;"italic"&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  @default "normal"&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontStyle", type="String", enumeration="normal,italic", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Sets the &amp;lt;code&amp;gt;thickness&amp;lt;/code&amp;gt; property of internal TextFields that represent text in Flex controls.&lt;br /&gt;*  This property specifies the thickness of the glyph edges.&lt;br /&gt;*  The possible values are Numbers from -200 to 200.&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This property only applies when you are using an embedded font&lt;br /&gt;*  and the &amp;lt;code&amp;gt;fontAntiAliasType&amp;lt;/code&amp;gt; property&lt;br /&gt;*  is set to &amp;lt;code&amp;gt;"advanced"&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  &amp;lt;p&amp;gt;This style has no effect on system fonts.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;This style applies to all the text in a TextField subcontrol;&lt;br /&gt;*  you can't apply it to some characters and not others.&amp;lt;/p&amp;gt;&lt;br /&gt;* &lt;br /&gt;*  @default 0&lt;br /&gt;* &lt;br /&gt;*  @see flash.text.TextField&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontThickness", type="Number", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Determines whether the text is boldface.&lt;br /&gt;*  Recognized values are &amp;lt;code&amp;gt;normal&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;bold&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value for Button controls is &amp;lt;code&amp;gt;bold&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value for all other controls is &amp;lt;code&amp;gt;normal&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="fontWeight", type="String", enumeration="normal,bold", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  A Boolean value that indicates whether kerning&lt;br /&gt;*  is enabled (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;) or disabled (&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;).&lt;br /&gt;*  Kerning adjusts the gap between certain character pairs&lt;br /&gt;*  to improve readability, and should be used only when necessary,&lt;br /&gt;*  such as with headings in large fonts.&lt;br /&gt;*  Kerning is supported for embedded fonts only.&lt;br /&gt;*  Certain fonts, such as Verdana, and monospaced fonts,&lt;br /&gt;*  such as Courier New, do not support kerning.&lt;br /&gt;*&lt;br /&gt;*  @default false&lt;br /&gt;*/&lt;br /&gt;[Style(name="kerning", type="Boolean", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  The number of additional pixels to appear between each character.&lt;br /&gt;*  A positive value increases the character spacing beyond the normal spacing,&lt;br /&gt;*  while a negative value decreases it.&lt;br /&gt;*&lt;br /&gt;*  @default 0&lt;br /&gt;*/&lt;br /&gt;[Style(name="letterSpacing", type="Number", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Alignment of text within a container.&lt;br /&gt;*  Possible values are &amp;lt;code&amp;gt;"left"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"right"&amp;lt;/code&amp;gt;,&lt;br /&gt;*  or &amp;lt;code&amp;gt;"center"&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for most components is &amp;lt;code&amp;gt;"left"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  For the FormItem component,&lt;br /&gt;*  the default value is &amp;lt;code&amp;gt;"right"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  For the Button, LinkButton, and AccordionHeader components,&lt;br /&gt;*  the default value is &amp;lt;code&amp;gt;"center"&amp;lt;/code&amp;gt;, and&lt;br /&gt;*  this property is only recognized when the&lt;br /&gt;*  &amp;lt;code&amp;gt;labelPlacement&amp;lt;/code&amp;gt; property is set to &amp;lt;code&amp;gt;"left"&amp;lt;/code&amp;gt; or&lt;br /&gt;*  &amp;lt;code&amp;gt;"right"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  If &amp;lt;code&amp;gt;labelPlacement&amp;lt;/code&amp;gt; is set to &amp;lt;code&amp;gt;"top"&amp;lt;/code&amp;gt; or&lt;br /&gt;*  &amp;lt;code&amp;gt;"bottom"&amp;lt;/code&amp;gt;, the text and any icon are centered.&amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="textAlign", type="String", enumeration="left,center,right", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Determines whether the text is underlined.&lt;br /&gt;*  Possible values are &amp;lt;code&amp;gt;"none"&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;"underline"&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  @default "none"&lt;br /&gt;*/&lt;br /&gt;[Style(name="textDecoration", type="String", enumeration="none,underline", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Offset of first line of text from the left side of the container, in pixels.&lt;br /&gt;*&lt;br /&gt;*  @default 0&lt;br /&gt;*/&lt;br /&gt;[Style(name="textIndent", type="Number", format="Length", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Number of pixels between the component's left border&lt;br /&gt;*  and the left edge of its content area.&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value is 0.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for a Button control is 10.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the ComboBox control is 5.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the Form container is 16.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the Tree control is 2.&amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="paddingLeft", type="Number", format="Length", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Number of pixels between the component's right border&lt;br /&gt;*  and the right edge of its content area.&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value is 0.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for a Button control is 10.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the ComboBox control is 5.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the Form container is 16.&amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="paddingRight", type="Number", format="Length", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Additional vertical space between lines of text.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value is 2.&amp;lt;/p&amp;gt;&lt;br /&gt;*  &amp;lt;p&amp;gt;The default value for the ComboBox control is 0.&amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="leading", type="Number", format="Length", inherit="yes")]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  The color for the icon in a skin.&lt;br /&gt;*  For example, this style is used by the CheckBoxIcon skin class&lt;br /&gt;*  to draw the check mark for a CheckBox control,&lt;br /&gt;*  by the ComboBoxSkin class to draw the down arrow of the ComboBox control,&lt;br /&gt;*  and by the DateChooserMonthArrowSkin skin class to draw the month arrow&lt;br /&gt;*  for the DateChooser control.&lt;br /&gt;*&lt;br /&gt;*  The default value depends on the component class;&lt;br /&gt;*  if it is not overridden by the class, the default value is &amp;lt;code&amp;gt;0x111111&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="iconColor", type="uint", format="Color", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  The color for the icon in a disabled skin.&lt;br /&gt;*  For example, this style is used by the CheckBoxIcon skin class&lt;br /&gt;*  to draw the check mark for a disabled CheckBox control,&lt;br /&gt;*  by the ComboBoxSkin class to draw the down arrow of a disabled ComboBox control,&lt;br /&gt;*  and by the DateChooserMonthArrowSkin skin class to draw the month arrow&lt;br /&gt;*  for a disabled DateChooser control.&lt;br /&gt;*&lt;br /&gt;*  The default value depends on the component class;&lt;br /&gt;*  if it is not overridden by the class, the default value is &amp;lt;code&amp;gt;0x999999&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="disabledIconColor", type="uint", format="Color", inherit="yes")]&lt;br /&gt;/**&lt;br /&gt;*  Specifies the alpha transparency value of the focus skin.&lt;br /&gt;* &lt;br /&gt;*  @default 0.4&lt;br /&gt;*/&lt;br /&gt;[Style(name="focusAlpha", type="Number", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Specifies which corners of the focus rectangle should be rounded.&lt;br /&gt;*  This value is a space-separated String that can contain any&lt;br /&gt;*  combination of &amp;lt;code&amp;gt;"tl"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"tr"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"bl"&amp;lt;/code&amp;gt;&lt;br /&gt;*  and &amp;lt;code&amp;gt;"br"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  For example, to specify that the right side corners should be rounded,&lt;br /&gt;*  but the left side corners should be square, use &amp;lt;code&amp;gt;"tr br"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The &amp;lt;code&amp;gt;cornerRadius&amp;lt;/code&amp;gt; style property specifies&lt;br /&gt;*  the radius of the rounded corners.&lt;br /&gt;*  The default value depends on the component class; if not overridden for&lt;br /&gt;*  the class, default value is &amp;lt;code&amp;gt;"tl tr bl br"&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="focusRoundedCorners", type="String", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Alpha level of the color defined by the &amp;lt;code&amp;gt;backgroundColor&amp;lt;/code&amp;gt;&lt;br /&gt;*  property, of the image or SWF file defined by the &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt;&lt;br /&gt;*  style.&lt;br /&gt;*  Valid values range from 0.0 to 1.0. For most controls, the default value is 1.0,&lt;br /&gt;*  but for ToolTip controls, the default value is 0.95 and for Alert controls, the default value is 0.9.&lt;br /&gt;* &lt;br /&gt;*  @default 1.0&lt;br /&gt;*/&lt;br /&gt;[Style(name="backgroundAlpha", type="Number", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Background color of a component.&lt;br /&gt;*  You can have both a &amp;lt;code&amp;gt;backgroundColor&amp;lt;/code&amp;gt; and a&lt;br /&gt;*  &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt; set.&lt;br /&gt;*  Some components do not have a background.&lt;br /&gt;*  The DataGrid control ignores this style.&lt;br /&gt;*  The default value is &amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;, which means it is not set.&lt;br /&gt;*  If both this style and the &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt; style&lt;br /&gt;*  are &amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;, the component has a transparent background.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;For the Application container, this style specifies the background color&lt;br /&gt;*  while the application loads, and a background gradient while it is running.&lt;br /&gt;*  Flex calculates the gradient pattern between a color slightly darker than&lt;br /&gt;*  the specified color, and a color slightly lighter than the specified color.&amp;lt;/p&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;The default skins of most Flex controls are partially transparent. As a result, the background color of&lt;br /&gt;*  a container partially "bleeds through" to controls that are in that container. You can avoid this by setting the&lt;br /&gt;*  alpha values of the control's &amp;lt;code&amp;gt;fillAlphas&amp;lt;/code&amp;gt; property to 1, as the following example shows:&lt;br /&gt;*  &amp;lt;pre&amp;gt;&lt;br /&gt;*  &amp;lt;mx:&amp;lt;i&amp;gt;Container&amp;lt;/i&amp;gt; backgroundColor="0x66CC66"/&amp;gt;&lt;br /&gt;*      &amp;lt;mx:&amp;lt;i&amp;gt;ControlName&amp;lt;/i&amp;gt; ... fillAlphas="[1,1]"/&amp;gt;&lt;br /&gt;*  &amp;lt;/mx:&amp;lt;i&amp;gt;Container&amp;lt;/i&amp;gt;&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;*  &amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="backgroundColor", type="uint", format="Color", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Background color of the component when it is disabled.&lt;br /&gt;*  The global default value is &amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value for List controls is &amp;lt;code&amp;gt;0xDDDDDD&amp;lt;/code&amp;gt; (light gray).&lt;br /&gt;*  If a container is disabled, the background is dimmed, and the degree of&lt;br /&gt;*  dimming is controlled by the &amp;lt;code&amp;gt;disabledOverlayAlpha&amp;lt;/code&amp;gt; style.&lt;br /&gt;*/&lt;br /&gt;[Style(name="backgroundDisabledColor", type="uint", format="Color", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Background image of a component.  This can be an absolute or relative&lt;br /&gt;*  URL or class.  You can either have both a &amp;lt;code&amp;gt;backgroundColor&amp;lt;/code&amp;gt; and a&lt;br /&gt;*  &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt; set at the same time. The background image is displayed&lt;br /&gt;*  on top of the background color.&lt;br /&gt;*  The default value is &amp;lt;code&amp;gt;undefined&amp;lt;/code&amp;gt;, meaning "not set".&lt;br /&gt;*  If this style and the &amp;lt;code&amp;gt;backgroundColor&amp;lt;/code&amp;gt; style are undefined,&lt;br /&gt;*  the component has a transparent background.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;The default skins of most Flex controls are partially transparent. As a result, the background image of&lt;br /&gt;*  a container partially "bleeds through" to controls that are in that container. You can avoid this by setting the&lt;br /&gt;*  alpha values of the control's &amp;lt;code&amp;gt;fillAlphas&amp;lt;/code&amp;gt; property to 1, as the following example shows:&lt;br /&gt;*  &amp;lt;pre&amp;gt;&lt;br /&gt;*  &amp;lt;mx:&amp;lt;i&amp;gt;Container&amp;lt;/i&amp;gt; backgroundColor="0x66CC66"/&amp;gt;&lt;br /&gt;*      &amp;lt;mx:&amp;lt;i&amp;gt;ControlName&amp;lt;/i&amp;gt; ... fillAlphas="[1,1]"/&amp;gt;&lt;br /&gt;*  &amp;lt;/mx:&amp;lt;i&amp;gt;Container&amp;lt;/i&amp;gt;&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;*  &amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="backgroundImage", type="Object", format="File", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Scales the image specified by &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt;&lt;br /&gt;*  to different percentage sizes.&lt;br /&gt;*  A value of &amp;lt;code&amp;gt;"100%"&amp;lt;/code&amp;gt; stretches the image&lt;br /&gt;*  to fit the entire component.&lt;br /&gt;*  To specify a percentage value, you must include the percent sign (%).&lt;br /&gt;*  The default for the Application container is &amp;lt;code&amp;gt;100%&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value for all other containers is &amp;lt;code&amp;gt;auto&amp;lt;/code&amp;gt;, which maintains&lt;br /&gt;*  the original size of the image.&lt;br /&gt;*/&lt;br /&gt;[Style(name="backgroundSize", type="String", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Color of the border.&lt;br /&gt;*  The default value depends on the component class;&lt;br /&gt;*  if not overridden for the class, the default value is &amp;lt;code&amp;gt;0xB7BABC&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="borderColor", type="uint", format="Color", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Bounding box sides.&lt;br /&gt;*  A space-delimited String that specifies the sides of the border to show.&lt;br /&gt;*  The String can contain &amp;lt;code&amp;gt;"left"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"top"&amp;lt;/code&amp;gt;,&lt;br /&gt;*  &amp;lt;code&amp;gt;"right"&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;"bottom"&amp;lt;/code&amp;gt; in any order.&lt;br /&gt;*  The default value is &amp;lt;code&amp;gt;"left top right bottom"&amp;lt;/code&amp;gt;,&lt;br /&gt;*  which shows all four sides.&lt;br /&gt;*&lt;br /&gt;*  This style is only used when borderStyle is &amp;lt;code&amp;gt;"solid"&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="borderSides", type="String", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  The border skin class of the component.&lt;br /&gt;*  The mx.skins.halo.HaloBorder class is the default value for all components&lt;br /&gt;*  that do not explicitly set their own default.&lt;br /&gt;*  The Panel container has a default value of mx.skins.halo.PanelSkin.&lt;br /&gt;*  To determine the default value for a component, see the default.css file.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.HaloBorder&lt;br /&gt;*/&lt;br /&gt;[Style(name="borderSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Bounding box style.&lt;br /&gt;*  The possible values are &amp;lt;code&amp;gt;"none"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"solid"&amp;lt;/code&amp;gt;,&lt;br /&gt;*  &amp;lt;code&amp;gt;"inset"&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;"outset"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value depends on the component class;&lt;br /&gt;*  if not overridden for the class, the default value is &amp;lt;code&amp;gt;"inset"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value for most Containers is &amp;lt;code&amp;gt;"none"&amp;lt;/code&amp;gt;.&lt;br /&gt;*/&lt;br /&gt;[Style(name="borderStyle", type="String", enumeration="inset,outset,solid,none", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Bounding box thickness.&lt;br /&gt;*  Only used when &amp;lt;code&amp;gt;borderStyle&amp;lt;/code&amp;gt; is set to &amp;lt;code&amp;gt;"solid"&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  @default 1&lt;br /&gt;*/&lt;br /&gt;[Style(name="borderThickness", type="Number", format="Length", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Radius of component corners.&lt;br /&gt;*  The default value depends on the component class;&lt;br /&gt;*  if not overriden for the class, the default value is 0.&lt;br /&gt;*  The default value for ApplicationControlBar is 5.&lt;br /&gt;*/&lt;br /&gt;[Style(name="cornerRadius", type="Number", format="Length", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Boolean property that specifies whether the component has a visible&lt;br /&gt;*  drop shadow.&lt;br /&gt;*  This style is used with &amp;lt;code&amp;gt;borderStyle="solid"&amp;lt;/code&amp;gt;.&lt;br /&gt;*  The default value is &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; For drop shadows to appear on containers, set&lt;br /&gt;*  &amp;lt;code&amp;gt;backgroundColor&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;backgroundImage&amp;lt;/code&amp;gt; properties.&lt;br /&gt;*  Otherwise, the shadow appears behind the container because&lt;br /&gt;*  the default background of a container is transparent.&amp;lt;/p&amp;gt;&lt;br /&gt;*/&lt;br /&gt;[Style(name="dropShadowEnabled", type="Boolean", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Color of the drop shadow.&lt;br /&gt;*&lt;br /&gt;*  @default 0x000000&lt;br /&gt;*/&lt;br /&gt;[Style(name="dropShadowColor", type="uint", format="Color", inherit="yes")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Direction of the drop shadow.&lt;br /&gt;*  Possible values are &amp;lt;code&amp;gt;"left"&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;"center"&amp;lt;/code&amp;gt;,&lt;br /&gt;*  and &amp;lt;code&amp;gt;"right"&amp;lt;/code&amp;gt;.&lt;br /&gt;*&lt;br /&gt;*  @default "center"&lt;br /&gt;*/&lt;br /&gt;[Style(name="shadowDirection", type="String", enumeration="left,center,right", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Distance of the drop shadow.&lt;br /&gt;*  If the property is set to a negative value, the shadow appears above the component.&lt;br /&gt;*&lt;br /&gt;*  @default 2&lt;br /&gt;*/&lt;br /&gt;[Style(name="shadowDistance", type="Number", format="Length", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the default skin for the down arrow.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperDownSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="downArrowSkin", type="Class", inherit="no", states="up, over, down, disabled")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Down arrow&lt;br /&gt;*  when the arrow is disabled.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperDownSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="downArrowDisabledSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Down arrow&lt;br /&gt;*  when the arrow is enabled and a user presses the mouse button over the arrow.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperDownSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="downArrowDownSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Down arrow&lt;br /&gt;*  when the arrow is enabled and the mouse pointer is over the arrow.&lt;br /&gt;* &lt;br /&gt;*  @default mx.skins.halo.NumberStepperDownSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="downArrowOverSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Down arrow&lt;br /&gt;*  when the arrow is enabled and the mouse pointer is not on the arrow.&lt;br /&gt;*  There is no default.&lt;br /&gt;*/&lt;br /&gt;[Style(name="downArrowUpSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Alphas used for the highlight fill of controls.&lt;br /&gt;*&lt;br /&gt;*  @default [ 0.3, 0.0 ]&lt;br /&gt;*/&lt;br /&gt;[Style(name="highlightAlphas", type="Array", arrayType="Number", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the default skin for the up arrow.&lt;br /&gt;* &lt;br /&gt;*  @default mx.skins.halo.NumberStepperUpSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="upArrowSkin", type="Class", inherit="no", states="up, over, down, disabled")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Up arrow&lt;br /&gt;*  when the arrow is disabled.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperUpSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="upArrowDisabledSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Up arrow&lt;br /&gt;*  when the arrow is enabled and a user presses the mouse button over the arrow.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperUpSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="upArrowDownSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Up arrow&lt;br /&gt;*  when the arrow is enabled and the mouse pointer is over the arrow.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperUpSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="upArrowOverSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;*  Name of the class to use as the skin for the Up arrow&lt;br /&gt;*  when the arrow is enabled and the mouse pointer is not on the arrow.&lt;br /&gt;*&lt;br /&gt;*  @default mx.skins.halo.NumberStepperUpSkin&lt;br /&gt;*/&lt;br /&gt;[Style(name="upArrowUpSkin", type="Class", inherit="no")]&lt;br /&gt;&lt;br /&gt;//--------------------------------------&lt;br /&gt;//  Other metadata&lt;br /&gt;//--------------------------------------&lt;br /&gt;&lt;br /&gt;[DefaultBindingProperty(source="value", destination="value")]&lt;br /&gt;&lt;br /&gt;[DefaultTriggerEvent("change")]&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* This is a custom stepper component based off the NumberStepper component in the flex framwork. &lt;br /&gt;* It is a compleate duplication of the internal class as the changes are to complex and core to be changed with overrides.&lt;br /&gt;* In short the value propertie was changed from a number to a Date and all associated calculations as well such as steping.&lt;br /&gt;* There are also additinal properties to configure the display format and&lt;br /&gt;* &amp;lt;pre&amp;gt;&lt;br /&gt;*  &amp;lt;mx:CustomStepper&lt;br /&gt;*    &amp;lt;strong&amp;gt;Properties&amp;lt;/strong&amp;gt;&lt;br /&gt;*     format=L:NN A|MM/DD/YYYY|Custom&lt;br /&gt;*     prependDate=false&lt;br /&gt;*     stepType=Date Property&lt;br /&gt;* &amp;lt;/pre&amp;gt;&lt;br /&gt;*&lt;br /&gt;* The formatedValue and selectedDate properties were also added witch return the value in different formats.&lt;br /&gt;*&lt;br /&gt;* @author Jeremy Pyne  &lt;jeremy pyne="" at="" gmail="" dot="" come=""&gt;&lt;br /&gt;*&lt;br /&gt;*  The NumberStepper control lets the user select&lt;br /&gt;*  a number from an ordered set.&lt;br /&gt;*  The NumberStepper control consists of a single-line&lt;br /&gt;*  input text field and a pair of arrow buttons&lt;br /&gt;*  for stepping through the possible values.&lt;br /&gt;*  The Up Arrow and Down Arrow keys also cycle through the values.&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;p&amp;gt;The NumberStepper control has the following default characteristics:&amp;lt;/p&amp;gt;&lt;br /&gt;*     &amp;lt;table class="innertable"&amp;gt;&lt;br /&gt;*        &amp;lt;tr&amp;gt;&lt;br /&gt;*           &amp;lt;th&amp;gt;Characteristic&amp;lt;/th&amp;gt;&lt;br /&gt;*           &amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&lt;br /&gt;*        &amp;lt;/tr&amp;gt;&lt;br /&gt;*        &amp;lt;tr&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Default size&amp;lt;/td&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Wide enough to display the maximum number of digits used by the control&amp;lt;/td&amp;gt;&lt;br /&gt;*        &amp;lt;/tr&amp;gt;&lt;br /&gt;*        &amp;lt;tr&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Minimum size&amp;lt;/td&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Based on the size of the text.&amp;lt;/td&amp;gt;&lt;br /&gt;*        &amp;lt;/tr&amp;gt;&lt;br /&gt;*        &amp;lt;tr&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Maximum size&amp;lt;/td&amp;gt;&lt;br /&gt;*           &amp;lt;td&amp;gt;Undefined&amp;lt;/td&amp;gt;&lt;br /&gt;*        &amp;lt;/tr&amp;gt;&lt;br /&gt;*     &amp;lt;/table&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  @mxml&lt;br /&gt;*&lt;br /&gt;*  The &amp;lt;code&amp;gt;&amp;lt;mx:NumberStepper&amp;gt;&amp;lt;/code&amp;gt; tag inherits all of the tag&lt;br /&gt;*  attributes of its superclass, and adds the following tag attributes:&lt;br /&gt;*&lt;br /&gt;*  &amp;lt;pre&amp;gt;&lt;br /&gt;*  &amp;lt;mx:NumberStepper&lt;br /&gt;*    &amp;lt;strong&amp;gt;Properties&amp;lt;/strong&amp;gt;&lt;br /&gt;*    imeMode="null"&lt;br /&gt;*    maxChars="10"&lt;br /&gt;*    maximum="10"&lt;br /&gt;*    minimum="0"&lt;br /&gt;*    stepSize="1"&lt;br /&gt;*    value="0"&lt;br /&gt;* &lt;br /&gt;*    &amp;lt;strong&amp;gt;Styles&amp;lt;/strong&amp;gt;&lt;br /&gt;*    backgroundAlpha="1.0"&lt;br /&gt;*    backgroundColor="undefined"&lt;br /&gt;*    backgroundImage="undefined"&lt;br /&gt;*    backgroundSize="auto"&lt;br /&gt;*    borderColor="0xAAB3B3"&lt;br /&gt;*    borderSides="left top right bottom"&lt;br /&gt;*    borderSkin="HaloBorder"&lt;br /&gt;*    borderStyle="inset"&lt;br /&gt;*    borderThickness="1"&lt;br /&gt;*    color="0x0B333C"&lt;br /&gt;*    cornerRadius="0"&lt;br /&gt;*    disabledColor="0xAAB3B3"&lt;br /&gt;*    disabledIconColor="0x999999"&lt;br /&gt;*    downArrowDisabledSkin="NumberStepperDownSkin"&lt;br /&gt;*    downArrowDownSkin="NumberStepperDownSkin"&lt;br /&gt;*    downArrowOverSkin="NumberStepperDownSkin"&lt;br /&gt;*    downArrowUpSkin="NumberStepperDownSkin"&lt;br /&gt;*    dropShadowEnabled="false"&lt;br /&gt;*    dropShadowColor="0x000000"&lt;br /&gt;*    focusAlpha="0.5"&lt;br /&gt;*    focusRoundedCorners="tl tr bl br"&lt;br /&gt;*    fontAntiAliasType="advanced"&lt;br /&gt;*    fontFamily="Verdana"&lt;br /&gt;*    fontGridFitType="pixel"&lt;br /&gt;*    fontSharpness="0"&lt;br /&gt;*    fontSize="10"&lt;br /&gt;*    fontStyle="normal|italic"&lt;br /&gt;*    fontThickness="0"&lt;br /&gt;*    fontWeight="normal|bold"&lt;br /&gt;*    highlightAlphas="[0.3,0.0]"&lt;br /&gt;*    iconColor="0x111111"&lt;br /&gt;*    leading="2"&lt;br /&gt;*    paddingLeft="0"&lt;br /&gt;*    paddingRight="0"&lt;br /&gt;*    shadowDirection="center"&lt;br /&gt;*    shadowDistance="2"&lt;br /&gt;*    textAlign="left|center|right"&lt;br /&gt;*    textDecoration="none|underline"&lt;br /&gt;*    textIndent="0"&lt;br /&gt;*    upArrowDisabledSkin="NumberStepperUpSkin"&lt;br /&gt;*    upArrowDownSkin="NumberStepperUpSkin"&lt;br /&gt;*    upArrowOverSkin="NumberStepperUpSkin"&lt;br /&gt;*    upArrowUpSkin="NumberStepperUpSkin"&lt;br /&gt;* &lt;br /&gt;*    &amp;lt;strong&amp;gt;Events&amp;lt;/strong&amp;gt;&lt;br /&gt;*    change="&amp;lt;i&amp;gt;No default&amp;lt;/i&amp;gt;"&lt;br /&gt;*    dataChange="&amp;lt;i&amp;gt;No default&amp;lt;/i&amp;gt;"&lt;br /&gt;*  /&amp;gt;&lt;br /&gt;*  &amp;lt;/pre&amp;gt;&lt;br /&gt;*&lt;br /&gt;*  @includeExample examples/NumberStepperExample.mxml&lt;br /&gt;*&lt;br /&gt;*/&lt;br /&gt;public class CustomStepper extends UIComponent&lt;br /&gt;                           implements IDataRenderer, IDropInListItemRenderer,&lt;br /&gt;                           IFocusManagerComponent, IIMESupport,&lt;br /&gt;                           IListItemRenderer&lt;br /&gt;{&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Constructor&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Constructor.&lt;br /&gt;    */&lt;br /&gt;   public function CustomStepper(type:String = "")&lt;br /&gt;   {&lt;br /&gt;       super();&lt;br /&gt;      &lt;br /&gt;       tabChildren = true;&lt;br /&gt;      &lt;br /&gt;       switch(type)&lt;br /&gt;       {&lt;br /&gt;        case TYPETIME:&lt;br /&gt;   stepType = "hours";&lt;br /&gt;   format = "L:NN A";&lt;br /&gt;   minimum.hours = 0;&lt;br /&gt;  &lt;br /&gt;   maximum = new Date(0);&lt;br /&gt;   maximum.hours = 23;&lt;br /&gt;  &lt;br /&gt;   var time:Date = new Date();&lt;br /&gt;   time.fullYear = 1969;&lt;br /&gt;   time.month = time.date = 0;&lt;br /&gt;   value = time;&lt;br /&gt;  &lt;br /&gt;   prependDate = true;&lt;br /&gt;  &lt;br /&gt;   break;&lt;br /&gt;  case TYPEDATE:&lt;br /&gt;   stepType = "date";&lt;br /&gt;   format="MM/DD/YYYY";&lt;br /&gt;  &lt;br /&gt;   minimum.date += 1;&lt;br /&gt;   minimum.hours = 0;&lt;br /&gt;  &lt;br /&gt;   var time:Date = new Date();&lt;br /&gt;   time.hours = time.minutes = time.seconds = time.seconds = 0;&lt;br /&gt;   value = time;&lt;br /&gt;   break&lt;br /&gt;  default:&lt;br /&gt;   break;&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Variables&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   mx_internal var inputField:TextInput;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   mx_internal var nextButton:Button;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   mx_internal var prevButton:Button;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Flag that will block default data/listData behavior&lt;br /&gt;    */&lt;br /&gt;   private var valueSet:Boolean;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Formatter used to render the text.&lt;br /&gt; */&lt;br /&gt;private var dateFormatter:DateFormatter = new DateFormatter();&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  format&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   [Bindable("formatChanged")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="")]&lt;br /&gt;   /**&lt;br /&gt;    *  Format of the text in the input box.&lt;br /&gt;    *&lt;br /&gt;    *  @default&lt;br /&gt;    */&lt;br /&gt;   public function get format():String&lt;br /&gt;   {&lt;br /&gt;       return dateFormatter.formatString;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public function set format(value:String):void&lt;br /&gt;   {&lt;br /&gt;       dateFormatter.formatString = value;&lt;br /&gt;      &lt;br /&gt;       dispatchEvent(new Event("formatChanged"));&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   public var prependDate:Boolean = false;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Step backward one step.&lt;br /&gt; */&lt;br /&gt;private static const STEPPREVIOUS:Number = -1;&lt;br /&gt;/**&lt;br /&gt; * Step forward one step.&lt;br /&gt; */&lt;br /&gt;private static const STEPNEXT:Number = 1;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Increment or decrement the passed in value by one step bassed on the stepType and stepSize.&lt;br /&gt; */&lt;br /&gt;private function step(curValue:Date, stepDirection:Number = STEPNEXT):Date&lt;br /&gt;{&lt;br /&gt; if(curValue == null)&lt;br /&gt;  return null;&lt;br /&gt;&lt;br /&gt; var newValue:Date = new Date(curValue.time)&lt;br /&gt; newValue[stepType] += stepSize * stepDirection;&lt;br /&gt; return newValue;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Make this control into a date stepper.&lt;br /&gt; */&lt;br /&gt;public static const TYPEDATE:String = "date";&lt;br /&gt;/**&lt;br /&gt; * Make this control into a time stepper.&lt;br /&gt; */&lt;br /&gt;public static const TYPETIME:String = "time";&lt;br /&gt;&lt;br /&gt;[Bindable("change")]&lt;br /&gt;   [Bindable("valueCommit")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="0")]&lt;br /&gt;/**&lt;br /&gt; * Get the properly formated value as dictated by format.&lt;br /&gt; */&lt;br /&gt;private function get formatedValue():String&lt;br /&gt;{&lt;br /&gt; return dateFormatter.format(value);&lt;br /&gt;}&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Overridden properties&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  baselinePosition&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  The baselinePosition of a NumberStepper is calculated&lt;br /&gt;    *  for its inputField.&lt;br /&gt;    */&lt;br /&gt;   override public function get baselinePosition():Number&lt;br /&gt;   {&lt;br /&gt;       if (FlexVersion.compatibilityVersion &amp;lt; FlexVersion.VERSION_3_0)&lt;br /&gt;           return inputField ? inputField.baselinePosition : NaN;&lt;br /&gt;&lt;br /&gt;       if (!validateBaselinePosition())&lt;br /&gt;           return NaN;&lt;br /&gt;&lt;br /&gt;       return inputField.y + inputField.baselinePosition;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  enabled&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private var enabledChanged:Boolean = false;&lt;br /&gt;&lt;br /&gt;   [Inspectable(category="General", enumeration="true,false", defaultValue="true")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override public function set enabled(value:Boolean):void&lt;br /&gt;   {&lt;br /&gt;       super.enabled = value;&lt;br /&gt;       enabledChanged = true;&lt;br /&gt;&lt;br /&gt;       invalidateProperties();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override public function get enabled():Boolean&lt;br /&gt;   {&lt;br /&gt;       return super.enabled;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  tabIndex&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the tabIndex property.&lt;br /&gt;    */&lt;br /&gt;   private var _tabIndex:int = -1;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private var tabIndexChanged:Boolean = false;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Tab order in which the control receives the focus when navigating&lt;br /&gt;    *  with the Tab key.&lt;br /&gt;    *&lt;br /&gt;    *  @default -1&lt;br /&gt;    *  @tiptext tabIndex of the component&lt;br /&gt;    *  @helpid 3198&lt;br /&gt;    */&lt;br /&gt;   override public function get tabIndex():int&lt;br /&gt;   {&lt;br /&gt;       return _tabIndex;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override public function set tabIndex(value:int):void&lt;br /&gt;   {&lt;br /&gt;       if (value == _tabIndex)&lt;br /&gt;           return;&lt;br /&gt;&lt;br /&gt;       _tabIndex = value;&lt;br /&gt;       tabIndexChanged = true;&lt;br /&gt;&lt;br /&gt;       invalidateProperties();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Properties&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  data&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the data property.&lt;br /&gt;    */&lt;br /&gt;   private var _data:Object;&lt;br /&gt;&lt;br /&gt;   [Bindable("dataChange")]&lt;br /&gt;   [Inspectable(environment="none")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  The &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; property lets you pass a value to the component&lt;br /&gt;    *  when you use it in an item renderer or item editor.&lt;br /&gt;    *  You typically use data binding to bind a field of the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;    *  property to a property of this component.&lt;br /&gt;    *&lt;br /&gt;    *  &amp;lt;p&amp;gt;When you use the control as a drop-in item renderer or drop-in&lt;br /&gt;    *  item editor, Flex automatically writes the current value of the item&lt;br /&gt;    *  to the &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; property of this control.&amp;lt;/p&amp;gt;&lt;br /&gt;    *&lt;br /&gt;    *  @default null&lt;br /&gt;    *  @see mx.core.IDataRenderer&lt;br /&gt;    */&lt;br /&gt;   public function get data():Object&lt;br /&gt;   {&lt;br /&gt;       if (!_listData)&lt;br /&gt;           data = this.value;&lt;br /&gt;&lt;br /&gt;       return _data;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set data(value:Object):void&lt;br /&gt;   {&lt;br /&gt;       _data = value;&lt;br /&gt;&lt;br /&gt;       if (!valueSet)&lt;br /&gt;       {&lt;br /&gt;           this.value = _listData ? _listData.label as Date : _data as Date;&lt;br /&gt;           valueSet = false;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  downArrowStyleFilters&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Set of styles to pass from the NumberStepper to the down arrow.&lt;br /&gt;    *  @see mx.styles.StyleProxy&lt;br /&gt;    */&lt;br /&gt;   protected function get downArrowStyleFilters():Object&lt;br /&gt;   {&lt;br /&gt;       return _downArrowStyleFilters;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   private static var _downArrowStyleFilters:Object =&lt;br /&gt;   {   &lt;br /&gt;       "cornerRadius" : "cornerRadius",       &lt;br /&gt;       "highlightAlphas" : "highlightAlphas",&lt;br /&gt;       "downArrowUpSkin" : "downArrowUpSkin",&lt;br /&gt;       "downArrowOverSkin" : "downArrowOverSkin",&lt;br /&gt;       "downArrowDownSkin" : "downArrowDownSkin",&lt;br /&gt;       "downArrowDisabledSkin" : "downArrowDisabledSkin",&lt;br /&gt;       "downArrowSkin" : "downArrowSkin",&lt;br /&gt;       "repeatDelay" : "repeatDelay",&lt;br /&gt;       "repeatInterval" : "repeatInterval"&lt;br /&gt;   };&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  imeMode&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private var _imeMode:String = null;&lt;br /&gt;&lt;br /&gt;   [Inspectable(defaultValue="")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Specifies the IME (Input Method Editor) mode.&lt;br /&gt;    *  The IME enables users to enter text in Chinese, Japanese, and Korean.&lt;br /&gt;    *  Flex sets the specified IME mode when the control gets the focus&lt;br /&gt;    *  and sets it back to previous value when the control loses the focus.&lt;br /&gt;    *&lt;br /&gt;    * &amp;lt;p&amp;gt;The flash.system.IMEConversionMode class defines constants for the&lt;br /&gt;    *  valid values for this property.&lt;br /&gt;    *  You can also specify &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; to specify no IME.&amp;lt;/p&amp;gt;&lt;br /&gt;    *&lt;br /&gt;    *  @see flash.system.IMEConversionMode&lt;br /&gt;    *&lt;br /&gt;    *  @default null&lt;br /&gt;    */&lt;br /&gt;   public function get imeMode():String&lt;br /&gt;   {&lt;br /&gt;       return _imeMode;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set imeMode(value:String):void&lt;br /&gt;   {&lt;br /&gt;       _imeMode = value;&lt;br /&gt;&lt;br /&gt;       if (inputField)&lt;br /&gt;           inputField.imeMode = _imeMode;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  inputFieldStyleFilters&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Set of styles to pass from the NumberStepper to the input field.&lt;br /&gt;    *  @see mx.styles.StyleProxy&lt;br /&gt;    */&lt;br /&gt;   protected function get inputFieldStyleFilters():Object&lt;br /&gt;   {&lt;br /&gt;       return _inputFieldStyleFilters;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   private static var _inputFieldStyleFilters:Object =&lt;br /&gt;   {&lt;br /&gt;       "backgroundAlpha" : "backgroundAlpha",&lt;br /&gt;       "backgroundColor" : "backgroundColor",&lt;br /&gt;       "backgroundImage" : "backgroundImage",&lt;br /&gt;       "backgroundDisabledColor" : "backgroundDisabledColor",&lt;br /&gt;       "backgroundSize" : "backgroundSize",&lt;br /&gt;       "borderAlpha" : "borderAlpha",&lt;br /&gt;       "borderColor" : "borderColor",&lt;br /&gt;       "borderSides" : "borderSides",&lt;br /&gt;       "borderSkin" : "borderSkin",&lt;br /&gt;       "borderStyle" : "borderStyle",&lt;br /&gt;       "borderThickness" : "borderThickness",&lt;br /&gt;       "dropShadowColor" : "dropShadowColor",&lt;br /&gt;       "dropShadowEnabled" : "dropShadowEnabled",&lt;br /&gt;       "embedFonts" : "embedFonts",&lt;br /&gt;       "focusAlpha" : "focusAlpha",&lt;br /&gt;       "focusBlendMode" : "focusBlendMode",&lt;br /&gt;       "focusRoundedCorners" : "focusRoundedCorners",&lt;br /&gt;       "focusThickness" : "focusThickness",&lt;br /&gt;       "paddingLeft" : "paddingLeft",&lt;br /&gt;       "paddingRight" : "paddingRight",&lt;br /&gt;       "shadowDirection" : "shadowDirection",&lt;br /&gt;       "shadowDistance" : "shadowDistance",&lt;br /&gt;       "textDecoration" : "textDecoration"&lt;br /&gt;   };&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  listData&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the listData property.&lt;br /&gt;    */&lt;br /&gt;   private var _listData:BaseListData;&lt;br /&gt;&lt;br /&gt;   [Bindable("dataChange")]&lt;br /&gt;   [Inspectable(environment="none")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  When a component is used as a drop-in item renderer or drop-in&lt;br /&gt;    *  item editor, Flex initializes the &amp;lt;code&amp;gt;listData&amp;lt;/code&amp;gt; property&lt;br /&gt;    *  of the component with the appropriate data from the List control.&lt;br /&gt;    *  The component can then use the &amp;lt;code&amp;gt;listData&amp;lt;/code&amp;gt; property&lt;br /&gt;    *  to initialize the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; property of the drop-in&lt;br /&gt;    *  item renderer or drop-in item editor.&lt;br /&gt;    *&lt;br /&gt;    *  &amp;lt;p&amp;gt;You do not set this property in MXML or ActionScript;&lt;br /&gt;    *  Flex sets it when the component is used as a drop-in item renderer&lt;br /&gt;    *  or drop-in item editor.&amp;lt;/p&amp;gt;&lt;br /&gt;    *&lt;br /&gt;    *  @default null&lt;br /&gt;    *  @see mx.controls.listClasses.IDropInListItemRenderer&lt;br /&gt;    */&lt;br /&gt;   public function get listData():BaseListData&lt;br /&gt;   {&lt;br /&gt;       return _listData;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set listData(value:BaseListData):void&lt;br /&gt;   {&lt;br /&gt;       _listData = value;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  maxChars&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the maxChars property.&lt;br /&gt;    */&lt;br /&gt;   private var _maxChars:int = 0;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private var maxCharsChanged:Boolean = false;&lt;br /&gt;&lt;br /&gt;   [Bindable("maxCharsChanged")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  The maximum number of characters that can be entered in the field.&lt;br /&gt;    *  A value of 0 means that any number of characters can be entered.&lt;br /&gt;    *&lt;br /&gt;    *  @default 0&lt;br /&gt;    */&lt;br /&gt;   public function get maxChars():int&lt;br /&gt;   {&lt;br /&gt;       return _maxChars;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public function set maxChars(value:int):void&lt;br /&gt;   {&lt;br /&gt;       if (value == _maxChars)&lt;br /&gt;           return;&lt;br /&gt;          &lt;br /&gt;       _maxChars = value;&lt;br /&gt;       maxCharsChanged = true;&lt;br /&gt;      &lt;br /&gt;       invalidateProperties();&lt;br /&gt;&lt;br /&gt;       dispatchEvent(new Event("maxCharsChanged"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  maximum&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for maximum property.&lt;br /&gt;    * Maximum 32 bit date.&lt;br /&gt;    */&lt;br /&gt;   private var _maximum:Date = new Date(2038, 1, 19);&lt;br /&gt;&lt;br /&gt;   [Bindable("maximumChanged")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Maximum value of the NumberStepper.&lt;br /&gt;    *  The maximum can be any number, including a fractional value.&lt;br /&gt;    *&lt;br /&gt;    *  @default 10&lt;br /&gt;    */&lt;br /&gt;   public function get maximum():Date&lt;br /&gt;   {&lt;br /&gt;       return _maximum;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public function set maximum(value:Date):void&lt;br /&gt;   {&lt;br /&gt;       _maximum = value;&lt;br /&gt;      &lt;br /&gt;       // To validate the value as min/max/stepsize has changed.&lt;br /&gt;       if (!valueChanged)&lt;br /&gt;       {&lt;br /&gt;           this.value = this.value;&lt;br /&gt;           valueSet = false;&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       dispatchEvent(new Event("maximumChanged"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  minimum&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for minimum property.&lt;br /&gt;    */&lt;br /&gt;   private var _minimum:Date = new Date(0);&lt;br /&gt;&lt;br /&gt;   [Bindable("minimumChanged")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Minimum value of the NumberStepper.&lt;br /&gt;    *  The minimum can be any number, including a fractional value.&lt;br /&gt;    *&lt;br /&gt;    *  @default 0&lt;br /&gt;    */&lt;br /&gt;   public function get minimum():Date&lt;br /&gt;   {&lt;br /&gt;       return _minimum;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public function set minimum(value:Date):void&lt;br /&gt;   {&lt;br /&gt;       _minimum = value;&lt;br /&gt;      &lt;br /&gt;       // To validate the value as min/max/stepsize has changed.&lt;br /&gt;       if (!valueChanged)&lt;br /&gt;       {&lt;br /&gt;           this.value = this.value;&lt;br /&gt;           valueSet = false;&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       dispatchEvent(new Event("minimumChanged"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  nextValue&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the nextValue property.&lt;br /&gt;    */&lt;br /&gt;   private var _nextValue:Date = null;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  The value that is one step larger than the current &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;&lt;br /&gt;    *  property and not greater than the &amp;lt;code&amp;gt;maximum&amp;lt;/code&amp;gt; property value.&lt;br /&gt;    */&lt;br /&gt;   public function get nextValue():Date&lt;br /&gt;   {&lt;br /&gt;       if (checkRange(step(value, STEPNEXT)))&lt;br /&gt;           _nextValue = step(value, STEPNEXT);&lt;br /&gt;&lt;br /&gt;       return _nextValue;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  previousValue&lt;br /&gt;   //----------------------------------&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the previousValue property.&lt;br /&gt;    */&lt;br /&gt;   private var _previousValue:Date = null;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  The value that is one step smaller than the current &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;&lt;br /&gt;    *  property and not smaller than the &amp;lt;code&amp;gt;maximum&amp;lt;/code&amp;gt; property value.&lt;br /&gt;    */&lt;br /&gt;   public function get previousValue():Date&lt;br /&gt;   {&lt;br /&gt;       if (checkRange(step(_value, STEPPREVIOUS)))&lt;br /&gt;           _previousValue = step(value, STEPPREVIOUS);&lt;br /&gt;&lt;br /&gt;       return _previousValue;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  stepSize&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the stepSize property.&lt;br /&gt;    */&lt;br /&gt;   private var _stepSize:Number = 1;&lt;br /&gt;&lt;br /&gt;   [Bindable("stepSizeChanged")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="1")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Non-zero unit of change between values.&lt;br /&gt;    *  The &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; property must be a multiple of this number.&lt;br /&gt;    *&lt;br /&gt;    *  @default 1&lt;br /&gt;    */&lt;br /&gt;   public function get stepSize():Number&lt;br /&gt;   {&lt;br /&gt;       return _stepSize;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set stepSize(value:Number):void&lt;br /&gt;   {&lt;br /&gt;       _stepSize = value;&lt;br /&gt;      &lt;br /&gt;       // To validate the value as min/max/stepsize has changed.&lt;br /&gt;       if (!valueChanged)&lt;br /&gt;       {&lt;br /&gt;           this.value = this.value;&lt;br /&gt;           valueSet = false;&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       dispatchEvent(new Event("stepSizeChanged"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  stepType&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the stepType property.&lt;br /&gt;    */&lt;br /&gt;   private var _stepType:String = "date";&lt;br /&gt;&lt;br /&gt;   [Bindable("stepSizeChanged")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="date")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Date field to step.&lt;br /&gt;    *  The &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; property must be a multiple of this number.&lt;br /&gt;    *&lt;br /&gt;    *  @default 1&lt;br /&gt;    */&lt;br /&gt;   public function get stepType():String&lt;br /&gt;   {&lt;br /&gt;       return _stepType;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set stepType(value:String):void&lt;br /&gt;   {&lt;br /&gt;       _stepType = value;&lt;br /&gt;      &lt;br /&gt;       // To validate the value as min/max/stepsize has changed.&lt;br /&gt;       if (!valueChanged)&lt;br /&gt;       {&lt;br /&gt;           this.value = this.value;&lt;br /&gt;           valueSet = false;&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       dispatchEvent(new Event("stepSizeChanged"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  upArrowStyleFilters&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Set of styles to pass from the NumberStepper to the up arrow.&lt;br /&gt;    *  @see mx.styles.StyleProxy&lt;br /&gt;    */&lt;br /&gt;   protected function get upArrowStyleFilters():Object&lt;br /&gt;   {&lt;br /&gt;       return _upArrowStyleFilters;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   private static var _upArrowStyleFilters:Object =&lt;br /&gt;   {&lt;br /&gt;       "cornerRadius" : "cornerRadius",       &lt;br /&gt;       "highlightAlphas" : "highlightAlphas",&lt;br /&gt;       "upArrowUpSkin" : "upArrowUpSkin",&lt;br /&gt;       "upArrowOverSkin" : "upArrowOverSkin",&lt;br /&gt;       "upArrowDownSkin" : "upArrowDownSkin",&lt;br /&gt;       "upArrowDisabledSkin" : "upArrowDisabledSkin",&lt;br /&gt;       "upArrowSkin" : "upArrowSkin",&lt;br /&gt;       "repeatDelay" : "repeatDelay",&lt;br /&gt;       "repeatInterval" : "repeatInterval"&lt;br /&gt;   };&lt;br /&gt;&lt;br /&gt;   //----------------------------------&lt;br /&gt;   //  value&lt;br /&gt;   //----------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Storage for the value property.&lt;br /&gt;    */&lt;br /&gt;   private var _value:Date = new Date();&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  last value we send CHANGE for.&lt;br /&gt;    *  _value will hold uncommitted values as well&lt;br /&gt;    */&lt;br /&gt;   private var lastValue:Date = new Date();&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Holds the value of the value property&lt;br /&gt;    *  until it is committed in commitProperties().&lt;br /&gt;    */&lt;br /&gt;   private var proposedValue:Date = new Date();&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Keeps track of whether we need to update&lt;br /&gt;    *  the value in commitProperties().&lt;br /&gt;    */&lt;br /&gt;   private var valueChanged:Boolean = false;&lt;br /&gt;&lt;br /&gt;   [Bindable("change")]&lt;br /&gt;   [Bindable("valueCommit")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="0")]&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  Current value displayed in the text area of the NumberStepper control.&lt;br /&gt;    *  If a user enters number that is not a multiple of the&lt;br /&gt;    *  &amp;lt;code&amp;gt;stepSize&amp;lt;/code&amp;gt; property or is not in the range&lt;br /&gt;    *  between the &amp;lt;code&amp;gt;maximum&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;minimum&amp;lt;/code&amp;gt; properties,&lt;br /&gt;    *  this property is set to the closest valid value.&lt;br /&gt;    *&lt;br /&gt;    *  @default 0&lt;br /&gt;    */&lt;br /&gt;   public function get value():Date&lt;br /&gt;   {&lt;br /&gt;       return valueChanged ? proposedValue : _value;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set value(value:Date):void&lt;br /&gt;   {&lt;br /&gt;       valueSet = true;&lt;br /&gt;&lt;br /&gt;       proposedValue = value;&lt;br /&gt;       valueChanged = true;&lt;br /&gt;&lt;br /&gt;       invalidateProperties();&lt;br /&gt;       invalidateSize();&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   [Bindable("change")]&lt;br /&gt;   [Bindable("valueCommit")]&lt;br /&gt;   [Inspectable(category="General", defaultValue="0")]&lt;br /&gt;   /**&lt;br /&gt;   * Alias value to selectedDate for drop in replacment or dateChoosers.&lt;br /&gt;   */&lt;br /&gt;    public function get selectedDate():Date&lt;br /&gt;   {&lt;br /&gt;       return valueChanged ? proposedValue : _value;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   public function set selectedDate(value:Date):void&lt;br /&gt;   {&lt;br /&gt;       valueSet = true;&lt;br /&gt;&lt;br /&gt;       proposedValue = value;&lt;br /&gt;       valueChanged = true;&lt;br /&gt;&lt;br /&gt;       invalidateProperties();&lt;br /&gt;       invalidateSize();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Overridden methods&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override protected function createChildren():void&lt;br /&gt;   {&lt;br /&gt;       super.createChildren();&lt;br /&gt;&lt;br /&gt;       if (!inputField)&lt;br /&gt;       {&lt;br /&gt;           inputField = new TextInput();&lt;br /&gt;&lt;br /&gt;           inputField.styleName = new StyleProxy(this, inputFieldStyleFilters);&lt;br /&gt;           inputField.focusEnabled = false;&lt;br /&gt;&lt;br /&gt;           // restrict to numbers - dashes - commas - decimals&lt;br /&gt;           inputField.restrict = "0-9\\- /:a-z";&lt;br /&gt;&lt;br /&gt;           inputField.maxChars = _maxChars;&lt;br /&gt;           inputField.text = dateFormatter.format(_value);&lt;br /&gt;           inputField.parentDrawsFocus = true;&lt;br /&gt;           inputField.imeMode = _imeMode;&lt;br /&gt;&lt;br /&gt;           inputField.addEventListener(FocusEvent.FOCUS_IN, inputField_focusInHandler);&lt;br /&gt;           inputField.addEventListener(FocusEvent.FOCUS_OUT, inputField_focusOutHandler);&lt;br /&gt;           inputField.addEventListener(KeyboardEvent.KEY_DOWN, inputField_keyDownHandler);&lt;br /&gt;           inputField.addEventListener(Event.CHANGE, inputField_changeHandler);&lt;br /&gt;&lt;br /&gt;           addChild(inputField);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       if (!nextButton)&lt;br /&gt;       {      &lt;br /&gt;           nextButton = new Button();&lt;br /&gt;           nextButton.styleName = new StyleProxy(this, upArrowStyleFilters);&lt;br /&gt;           nextButton.upSkinName = "upArrowUpSkin";&lt;br /&gt;           nextButton.overSkinName = "upArrowOverSkin";&lt;br /&gt;           nextButton.downSkinName = "upArrowDownSkin";&lt;br /&gt;           nextButton.disabledSkinName = "upArrowDisabledSkin";&lt;br /&gt;           nextButton.skinName = "upArrowSkin";&lt;br /&gt;           nextButton.upIconName = "";&lt;br /&gt;           nextButton.overIconName = "";&lt;br /&gt;           nextButton.downIconName = "";&lt;br /&gt;           nextButton.disabledIconName = "";&lt;br /&gt;&lt;br /&gt;           nextButton.focusEnabled = false;&lt;br /&gt;           nextButton.autoRepeat = true;&lt;br /&gt;&lt;br /&gt;           nextButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);&lt;br /&gt;           nextButton.addEventListener(FlexEvent.BUTTON_DOWN, buttonDownHandler);&lt;br /&gt;&lt;br /&gt;           addChild(nextButton);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       if (!prevButton)&lt;br /&gt;       {&lt;br /&gt;           prevButton = new Button();&lt;br /&gt;           prevButton.styleName = new StyleProxy(this, downArrowStyleFilters);&lt;br /&gt;           prevButton.upSkinName = "downArrowUpSkin";&lt;br /&gt;           prevButton.overSkinName = "downArrowOverSkin";&lt;br /&gt;           prevButton.downSkinName = "downArrowDownSkin";&lt;br /&gt;           prevButton.disabledSkinName = "downArrowDisabledSkin";&lt;br /&gt;           prevButton.skinName = "downArrowSkin";&lt;br /&gt;           prevButton.upIconName = "";&lt;br /&gt;           prevButton.overIconName = "";&lt;br /&gt;           prevButton.downIconName = "";&lt;br /&gt;           prevButton.disabledIconName = "";&lt;br /&gt;&lt;br /&gt;           prevButton.focusEnabled = false;&lt;br /&gt;           prevButton.autoRepeat = true;&lt;br /&gt;&lt;br /&gt;           prevButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);&lt;br /&gt;           prevButton.addEventListener(FlexEvent.BUTTON_DOWN, buttonDownHandler);&lt;br /&gt;&lt;br /&gt;           addChild(prevButton);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override protected function commitProperties():void&lt;br /&gt;   {&lt;br /&gt;       super.commitProperties();&lt;br /&gt;      &lt;br /&gt;       if (maxCharsChanged)&lt;br /&gt;       {&lt;br /&gt;           maxCharsChanged = false;&lt;br /&gt;           inputField.maxChars = _maxChars;&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       if (valueChanged)&lt;br /&gt;       {&lt;br /&gt;           valueChanged = false;&lt;br /&gt;&lt;br /&gt;           setValue(proposedValue, false);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       if (enabledChanged)&lt;br /&gt;       {&lt;br /&gt;           enabledChanged = false;&lt;br /&gt;&lt;br /&gt;           prevButton.enabled = enabled;&lt;br /&gt;           nextButton.enabled = enabled;&lt;br /&gt;           inputField.enabled = enabled;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       if (tabIndexChanged)&lt;br /&gt;       {&lt;br /&gt;           inputField.tabIndex = _tabIndex;&lt;br /&gt;&lt;br /&gt;           tabIndexChanged = false;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Return the preferred sizes of the stepper.&lt;br /&gt;    */&lt;br /&gt;   override protected function measure():void&lt;br /&gt;   {&lt;br /&gt;       super.measure();&lt;br /&gt;&lt;br /&gt;       var widestNumber:Date = dateFormatter.format(minimum).length &amp;gt;&lt;br /&gt;                                 dateFormatter.format(maximum).length ?&lt;br /&gt;                                 minimum :&lt;br /&gt;                                 maximum;&lt;br /&gt;&lt;br /&gt;       var lineMetrics:TextLineMetrics = measureText(dateFormatter.format(checkValidValue(widestNumber)));&lt;br /&gt;      &lt;br /&gt;       var textHeight:Number = inputField.getExplicitOrMeasuredHeight();&lt;br /&gt;       var buttonHeight:Number = prevButton.getExplicitOrMeasuredHeight() +&lt;br /&gt;                                 nextButton.getExplicitOrMeasuredHeight();&lt;br /&gt;&lt;br /&gt;       var h:Number = Math.max(textHeight, buttonHeight);&lt;br /&gt;       h = Math.max(DEFAULT_MEASURED_MIN_HEIGHT, h);&lt;br /&gt;&lt;br /&gt;       var textWidth:Number = lineMetrics.width + UITextField.TEXT_WIDTH_PADDING;&lt;br /&gt;       var buttonWidth:Number = Math.max(prevButton.getExplicitOrMeasuredWidth(),&lt;br /&gt;                                         nextButton.getExplicitOrMeasuredWidth());&lt;br /&gt;&lt;br /&gt;       var w:Number = textWidth + buttonWidth + 20;&lt;br /&gt;       w = Math.max(DEFAULT_MEASURED_MIN_WIDTH, w);&lt;br /&gt;&lt;br /&gt;       measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;&lt;br /&gt;       measuredMinHeight = DEFAULT_MEASURED_MIN_HEIGHT;&lt;br /&gt;&lt;br /&gt;       measuredWidth = w;&lt;br /&gt;       measuredHeight = h;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Place the buttons to the right of the text field.&lt;br /&gt;    */&lt;br /&gt;   override protected function updateDisplayList(unscaledWidth:Number,&lt;br /&gt;                                                 unscaledHeight:Number):void&lt;br /&gt;   {&lt;br /&gt;       super.updateDisplayList(unscaledWidth, unscaledHeight);&lt;br /&gt;&lt;br /&gt;       var w:Number = nextButton.getExplicitOrMeasuredWidth();&lt;br /&gt;       var h:Number = Math.round(unscaledHeight / 2);&lt;br /&gt;       var h2:Number = unscaledHeight - h;&lt;br /&gt;&lt;br /&gt;       nextButton.x = unscaledWidth - w;&lt;br /&gt;       nextButton.y = 0;&lt;br /&gt;       nextButton.setActualSize(w, h2);&lt;br /&gt;      &lt;br /&gt;       prevButton.x = unscaledWidth - w;&lt;br /&gt;       prevButton.y = unscaledHeight - h;&lt;br /&gt;       prevButton.setActualSize(w, h);&lt;br /&gt;&lt;br /&gt;       inputField.setActualSize(unscaledWidth - w, unscaledHeight);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Update the text field.&lt;br /&gt;    */&lt;br /&gt;   override public function setFocus():void&lt;br /&gt;   {&lt;br /&gt;       if (stage)&lt;br /&gt;           stage.focus = TextField(inputField.getTextField());&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   override protected function isOurFocus(target:DisplayObject):Boolean&lt;br /&gt;   {&lt;br /&gt;       return target == inputField || super.isOurFocus(target);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Methods&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Verify that the value is within range.&lt;br /&gt;    */&lt;br /&gt;   private function checkRange(v:Date):Boolean&lt;br /&gt;   {&lt;br /&gt;       return v.time &amp;gt;= minimum.time &amp;amp;&amp;amp; v.time &amp;lt;= maximum.time;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function checkValidValue(value:Date):Date&lt;br /&gt;   {&lt;br /&gt;       if (isNaN(value.time))&lt;br /&gt;           return this.value;&lt;br /&gt;&lt;br /&gt;       var closest:Date = new Date(value.time);&lt;br /&gt;       switch(stepType)&lt;br /&gt;       {&lt;br /&gt;        case "fullYear":&lt;br /&gt;         closest.month = 0;&lt;br /&gt;        case "month":&lt;br /&gt;         closest.date = 1;&lt;br /&gt;        case "date":&lt;br /&gt;         closest.hours = 0;&lt;br /&gt;        case "day":&lt;br /&gt;         closest.hours = 0;&lt;br /&gt;        case "hours":&lt;br /&gt;         closest.minutes = 0;&lt;br /&gt;        case "minutes":&lt;br /&gt;         closest.seconds = 0;&lt;br /&gt;        case "seconds":&lt;br /&gt;         closest.milliseconds = 0;&lt;br /&gt;        case "milliseconds":&lt;br /&gt;       }&lt;br /&gt;      &lt;br /&gt;       /*stepSize * Math.round(value / stepSize);&lt;br /&gt;&lt;br /&gt;       // The following logic has been implemented to fix bug 135045.&lt;br /&gt;       // It assumes that the above line of code which rounds off the&lt;br /&gt;       // value is not removed ! (That is, the precision of the value is&lt;br /&gt;       // never expected to be greater than the step size.&lt;br /&gt;       // ex : value = 1.11111 stepSize = 0.01)&lt;br /&gt;&lt;br /&gt;       // Use precision of the step to round of the value.&lt;br /&gt;       // When the stepSize is very small the system tends to put it in&lt;br /&gt;       // exponential format.(ex : 1E-7) The following string split logic&lt;br /&gt;       // cannot work with exponential notation. Hence we add 1 to the stepSize&lt;br /&gt;       // to make it get represented in the decimal format.&lt;br /&gt;       // We are only interested in the number of digits towards the right&lt;br /&gt;       // of the decimal place so it doesnot affect anything else.&lt;br /&gt;       var parts:Array = (new String(1 + stepSize)).split(".");&lt;br /&gt;&lt;br /&gt;       // we need to do the round of (to remove the floating point error)&lt;br /&gt;       // if the stepSize had a fractional value&lt;br /&gt;       if (parts.length == 2)&lt;br /&gt;       {&lt;br /&gt;           var scale:Number = Math.pow(10, parts[1].length);&lt;br /&gt;           closest = Math.round(closest * scale) / scale;&lt;br /&gt;       }*/&lt;br /&gt;&lt;br /&gt;       if (closest.time &amp;gt; maximum.time)&lt;br /&gt;           return maximum;&lt;br /&gt;       else if (closest.time &amp;lt; minimum.time)&lt;br /&gt;           return minimum;&lt;br /&gt;       else&lt;br /&gt;           return closest;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function setValue(value:Date,&lt;br /&gt;                             sendEvent:Boolean = true,&lt;br /&gt;                             trigger:Event = null):void&lt;br /&gt;   {&lt;br /&gt;&lt;br /&gt;       var v:Date = checkValidValue(value);&lt;br /&gt;       if (v == lastValue)&lt;br /&gt;           return;&lt;br /&gt;&lt;br /&gt;       lastValue = _value = v;&lt;br /&gt;       inputField.text = dateFormatter.format(v);&lt;br /&gt;      &lt;br /&gt;       //Hack to fix 7:00  time not rendering proerly&lt;br /&gt;       if(inputField.text == "")&lt;br /&gt;        inputField.text = v.toLocaleTimeString().replace(":00 ", " ").replace("07", "7");&lt;br /&gt;&lt;br /&gt;       if (sendEvent)&lt;br /&gt;       {&lt;br /&gt;           var event:CustomStepperEvent =&lt;br /&gt;               new CustomStepperEvent(CustomStepperEvent.CHANGE);&lt;br /&gt;           event.value = _value;&lt;br /&gt;           event.triggerEvent = trigger;&lt;br /&gt;&lt;br /&gt;           dispatchEvent(event);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Checks the value in the text field. If it is valid value&lt;br /&gt;    *  and different from the current value, it is taken as new value.&lt;br /&gt;    */&lt;br /&gt;   private function takeValueFromTextField(trigger:Event = null):void&lt;br /&gt;   {&lt;br /&gt;    var inputValue:Date = new Date((prependDate ? value.toDateString() + " " : "") + inputField.text);&lt;br /&gt;       if ((inputValue.time != lastValue.time &amp;amp;&amp;amp;&lt;br /&gt;           (Math.abs(inputValue.time - lastValue.time) &amp;gt;= 0.000001 || inputValue == null)) ||&lt;br /&gt;           inputField.text == "")&lt;br /&gt;       {&lt;br /&gt;           var newValue:Date = checkValidValue(new Date((prependDate ? value.toDateString() + " " : "") + inputField.text));&lt;br /&gt;           inputField.text = dateFormatter.format(newValue);&lt;br /&gt;           setValue(newValue, trigger != null, trigger);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Increase/decrease the current value.&lt;br /&gt;    */&lt;br /&gt;   private function buttonPress(button:Button, trigger:Event = null):void&lt;br /&gt;   {&lt;br /&gt;       if (enabled)&lt;br /&gt;       {&lt;br /&gt;           // we may get a buttonPress message before focusOut event for&lt;br /&gt;           // the text field. Hence we need to check the value in&lt;br /&gt;           // inputField.&lt;br /&gt;           takeValueFromTextField();&lt;br /&gt;&lt;br /&gt;           var oldValue:Date = lastValue;&lt;br /&gt;           setValue(button == nextButton ?&lt;br /&gt;                    step(lastValue, STEPNEXT) :&lt;br /&gt;                    step(lastValue, STEPPREVIOUS), true, trigger);&lt;br /&gt;&lt;br /&gt;           if (oldValue.time != lastValue.time)&lt;br /&gt;               inputField.getTextField().setSelection(0,0);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Overridden event handlers: UIComponent&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Remove the focus from the text field.&lt;br /&gt;    */&lt;br /&gt;   override protected function focusInHandler(event:FocusEvent):void&lt;br /&gt;   {&lt;br /&gt;       super.focusInHandler(event);&lt;br /&gt;&lt;br /&gt;       var fm:IFocusManager = focusManager;&lt;br /&gt;       if (fm)&lt;br /&gt;           fm.defaultButtonEnabled = false;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    *  Remove the focus from the text field.&lt;br /&gt;    */&lt;br /&gt;   override protected function focusOutHandler(event:FocusEvent):void&lt;br /&gt;   {&lt;br /&gt;       var fm:IFocusManager = focusManager;&lt;br /&gt;       if (fm)&lt;br /&gt;           fm.defaultButtonEnabled = true;&lt;br /&gt;&lt;br /&gt;       super.focusOutHandler(event);&lt;br /&gt;&lt;br /&gt;       takeValueFromTextField(event);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;   //&lt;br /&gt;   //  Event handlers&lt;br /&gt;   //&lt;br /&gt;   //--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function buttonDownHandler(event:FlexEvent):void&lt;br /&gt;   {&lt;br /&gt;       buttonPress(Button(event.currentTarget), event);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function buttonClickHandler(event:MouseEvent):void&lt;br /&gt;   {&lt;br /&gt;       inputField.setFocus();&lt;br /&gt;       inputField.getTextField().setSelection(0, 0);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function inputField_focusInHandler(event:FocusEvent):void&lt;br /&gt;   {&lt;br /&gt;       focusInHandler(event);&lt;br /&gt;      &lt;br /&gt;       // Send out a new FocusEvent because the TextInput eats the event&lt;br /&gt;       // Make sure that it does not bubble.&lt;br /&gt;       dispatchEvent(new FocusEvent(event.type, false, false,&lt;br /&gt;                                    event.relatedObject,&lt;br /&gt;                                    event.shiftKey, event.keyCode));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function inputField_focusOutHandler(event:FocusEvent):void&lt;br /&gt;   {&lt;br /&gt;       focusOutHandler(event);&lt;br /&gt;      &lt;br /&gt;       // Send out a new FocusEvent because the TextInput eats the event&lt;br /&gt;       // Make sure that it does not bubble&lt;br /&gt;       dispatchEvent(new FocusEvent(event.type, false, false,&lt;br /&gt;                                    event.relatedObject,&lt;br /&gt;                                    event.shiftKey,event.keyCode));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function inputField_keyDownHandler(event:KeyboardEvent):void&lt;br /&gt;   {&lt;br /&gt;       var tmpV:Date;&lt;br /&gt;&lt;br /&gt;       switch (event.keyCode)&lt;br /&gt;       {&lt;br /&gt;           case Keyboard.DOWN:&lt;br /&gt;           {&lt;br /&gt;               tmpV = step(value, STEPPREVIOUS);&lt;br /&gt;               setValue(tmpV, true);&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           case Keyboard.UP:&lt;br /&gt;           {&lt;br /&gt;               tmpV = step(value, STEPNEXT);&lt;br /&gt;               setValue(tmpV, true);&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           case Keyboard.HOME:&lt;br /&gt;           {&lt;br /&gt;               inputField.text = dateFormatter.format(minimum);&lt;br /&gt;               setValue(minimum, true);&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           case Keyboard.END:&lt;br /&gt;           {&lt;br /&gt;               inputField.text = dateFormatter.format(maximum);&lt;br /&gt;               setValue(maximum, true);&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;           case Keyboard.ENTER:&lt;br /&gt;           case Keyboard.TAB:&lt;br /&gt;           {&lt;br /&gt;            var inputValue:Date = new Date((prependDate ? value.toDateString() + " " : "") + inputField.text);&lt;br /&gt;               if (inputValue.time != lastValue.time &amp;amp;&amp;amp;&lt;br /&gt;                   (Math.abs(inputValue.time - lastValue.time) &amp;gt;= 0.000001 ||&lt;br /&gt;                     isNaN(inputValue.time)))&lt;br /&gt;               {&lt;br /&gt;                   var newValue:Date = checkValidValue(new Date((prependDate ? value.toDateString() + " " : "") + inputField.text));&lt;br /&gt;                   inputField.text = dateFormatter.format(newValue);&lt;br /&gt;                   setValue(newValue, true);&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;               // Prevent the defaultButton from firing&lt;br /&gt;               event.stopImmediatePropagation();&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // Act as a proxy because the TextInput stops propogation&lt;br /&gt;       dispatchEvent(event);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    *  @private&lt;br /&gt;    */&lt;br /&gt;   private function inputField_changeHandler(event:Event):void&lt;br /&gt;   {&lt;br /&gt;       // Stop the event from bubbling up.&lt;br /&gt;       event.stopImmediatePropagation();&lt;br /&gt;&lt;br /&gt;       var inputValue:Date = new Date((prependDate ? value.toDateString() + " " : "") + inputField.text);&lt;br /&gt;       if ((inputValue.time != value.time &amp;amp;&amp;amp;&lt;br /&gt;           (Math.abs(inputValue.time - value.time) &amp;gt;= 0.000001 || isNaN(inputValue.time))) ||&lt;br /&gt;           inputField.text == "")&lt;br /&gt;       {&lt;br /&gt;           _value = checkValidValue(inputValue);&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2813010811549257862?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2813010811549257862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2813010811549257862&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2813010811549257862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2813010811549257862'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/02/flex-date-and-time-stepper-components.html' title='Flex Date and Time Stepper Components'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4802415503296998061</id><published>2009-02-23T15:58:00.003-05:00</published><updated>2009-02-23T17:09:43.129-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX Dock'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>OS X Style Dock for Linux</title><content type='html'>So I recently switched to use Ubuntu at work and recently switched from the gnome task list to an OS X style dock.  After playing around with &lt;a href="http://www.blogger.com/%20http://do.davebsd.com/wiki/index.php?title=Main_Page"&gt;Gnome-Do&lt;/a&gt; for a while I ended up settling on &lt;a href="http://wiki.awn-project.org/"&gt;Avant Window Navigator&lt;/a&gt; instead.  Awn turned out to be slightly more configurable but the Gnome-Do project is relatively new.  So here are a few thoughts on both:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Gnome-Do:&lt;/span&gt;&lt;br /&gt;To install a version of gnome-do with the docky theme I first had to add the following repositories.&lt;br /&gt;&lt;pre&gt;deb http://ppa.launchpad.net/do-core/ppa/ubuntu intrepid main&lt;br /&gt;deb-src http://ppa.launchpad.net/do-core/ppa/ubuntu intrepid main&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then install gnome-do:&lt;br /&gt;&lt;pre&gt;sudo apt-get install gnome-do&lt;/pre&gt;&lt;br /&gt;Once installed open up Gnome-Do and switch the Theme to Docky.  This is smiler the OS X dock and also has Quicksilver style searching.  &lt;br /&gt;&lt;br /&gt;This tool has a nice layout and a very powerful text based action system but there is currently only the primary theme and very few options to control the UI.   It does Stack multiple instances of an application together but they window manager control of that stack is less then optimal.  Also the dock itself is part of the theme not the main program so there are no way to extend the dock with plug-ins.  It is a a simple list of shortcuts and running applications.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Avant Window Navigator&lt;/span&gt;&lt;br /&gt;To install awn just run:&lt;br /&gt;&lt;pre&gt;sudo apt-get install avant-window-navigator&lt;/pre&gt;&lt;br /&gt;This is a much more feature rick dock utility with a multitude of options and plugins, minus all the Quicksilver features.&lt;br /&gt;&lt;br /&gt;This dock did take a bit more to get configured though, I have two monitors and where Gnome-Do automatically came up on my primary display awn favored the left most monitor witch happens to be my  secondary monitor.  There was no apparent way to drag it to teh correct locations nor option to change it.  After a bit of fiddling I wound two settings that worked for me.  To fix this problem I used gconf-editor to change the following properties.&lt;br /&gt;&lt;pre&gt;/apps/avant-window-navigator/monitor_height = 1050&lt;br /&gt;/apps/avant-window-navigator/monitor_width = 3360&lt;br /&gt;/apps/avant-window-navigator/force_monitor = true&lt;br /&gt;/apps/avant-window-navigator/bar/bar_pos = .74&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first two settings configure the virtual screen size taken from gnome-display-properties and the third option causes awn to use the virtual screen size instead of the size of the first monitor.  The final option adjusts the position the dock, now centered between the two screens, to the center of the right screen.&lt;br /&gt;&lt;br /&gt;Note: The property editor was rounding .75 to .7 so I actually had to run the following command to set the property.&lt;br /&gt;&lt;pre&gt;gconftool-2 --set /apps/avant-window-navigator/bar/bar_pos .74 --type float&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As it turned out awn has its own share of hiccups including:&lt;br /&gt;There is no way to get multiple instance of the same application to stack up nicely.&lt;br /&gt;Multiple windows sharing the same name will cause the process icons to fall away and reappear at the end of the list.  This is visually confusing but would not be an issue if process could stack.&lt;br /&gt;&lt;br /&gt;That said there are many nice plugins for the dock including a embedded terminal and a multitude of themes and visual tweaks, though I wish there was an option to make auto-hide only hide a set distance instead of completely hiding the bar.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4802415503296998061?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4802415503296998061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4802415503296998061&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4802415503296998061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4802415503296998061'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/02/os-x-style-dock-for-linux.html' title='OS X Style Dock for Linux'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7411228274382753054</id><published>2009-02-16T14:05:00.004-05:00</published><updated>2009-02-23T16:27:00.204-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Fancy move/copy commands</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Upadte- 2/23/2009 4:25 PM&lt;/span&gt;&lt;br /&gt;Updated the action detection to work for the mv variant as well.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;br /&gt;Ok, here is a nice wrapper script I wrote in bash.  It adds a nice progress bar tot he cp and mv commands Linux.  &lt;br /&gt;&lt;br /&gt;To install just save this file someplace and give it execute rights.  Then create symbolic links for cp and mv in your $PATH.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$&gt; mkdir ~/bin&lt;br /&gt;Save as ~/bin/nice.ops&lt;br /&gt;$&gt; ln -s ~/bin/nice.ops ~/bin/cp&lt;br /&gt;$&gt; ln -s ~/bin/nice.ops ~/bin/mv&lt;br /&gt;Added ~/bin to $PATH in ~/.bashrc&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Code:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;nice.ops&lt;/span&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;# This is a custom wrapper for the cp and mv commands in *nix.  It adds a nice progress bar to the file operation.&lt;br /&gt;# Jeremy Pyne &lt;jeremy dot pyne at gmail dot com&gt;&lt;br /&gt;&lt;br /&gt;# Config, Set command mode here if auto-detect doesn't work.&lt;br /&gt;action= # cp|mv&lt;br /&gt;message= # Copying|Moving&lt;br /&gt;&lt;br /&gt;# Auto-detect command type.&lt;br /&gt;if [ -z $action ] ; then&lt;br /&gt; case `expr "$0" : '.*\(/cp\|/mv\)'` in&lt;br /&gt; /cp)&lt;br /&gt;  action=cp&lt;br /&gt;  message=Copying&lt;br /&gt;  ;;&lt;br /&gt; /mv)&lt;br /&gt;  action=mv&lt;br /&gt;  message=Moving&lt;br /&gt;  ;;&lt;br /&gt; esac&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Default Variables&lt;br /&gt;dest=&lt;br /&gt;quiet=0&lt;br /&gt;detail=0&lt;br /&gt;dest_dir=0&lt;br /&gt;dest_size=0&lt;br /&gt;pct=0&lt;br /&gt;&lt;br /&gt;# Throw a usage message if there are to few parameters.&lt;br /&gt;if [ $#  -lt 2 ] ; then&lt;br /&gt; /bin/$action $@&lt;br /&gt; exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Loop through each argument.&lt;br /&gt;for arg ; do&lt;br /&gt;        case $arg in&lt;br /&gt;  # Show extra details.&lt;br /&gt;                --detail)&lt;br /&gt;                        detail=1&lt;br /&gt;                        continue ;;&lt;br /&gt;  # Show less details.&lt;br /&gt;                --quiet)&lt;br /&gt;                        quiet=1&lt;br /&gt;                        continue ;;&lt;br /&gt;        esac&lt;br /&gt; &lt;br /&gt; # If there was a destination, change it to a source file.&lt;br /&gt;        if [ "$dest" ] ; then&lt;br /&gt;  files[${#files[@]}]="$dest"&lt;br /&gt;        fi&lt;br /&gt;&lt;br /&gt; # Set the current item as the destination.&lt;br /&gt;        dest="$arg"&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;# Calculate the source file cound and size.&lt;br /&gt;orig_size=`du -s "${files[@]}" | awk 'BEGIN{total=0}{total+=$1}END{print total}'`&lt;br /&gt;orig_count=`find "${files[@]}" | awk 'BEGIN{total=0}{total++}END{print total}'`&lt;br /&gt;&lt;br /&gt;# Show a info message.&lt;br /&gt;if [ $quiet -eq 0 ] ; then&lt;br /&gt;        echo $message $orig_count files $orig_size bytes to $dest.&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# If a single source directory is being used, it will be renamed so we need to gets it's contents.&lt;br /&gt;if [ ${#files[@]} -eq 1 -a -d "${files[0]}" ] ; then&lt;br /&gt; # Flag the rename bit se we can properly look for files.&lt;br /&gt;        dest_dir=1&lt;br /&gt; IFS=$'\n'&lt;br /&gt; # Store each direct child of the source directory.&lt;br /&gt; for file in `cd "${files[@]}" &amp;&amp; ls ` ; do&lt;br /&gt;  ofiles[${#ofiles[@]}]="$file"&lt;br /&gt; done&lt;br /&gt; IFS=$' '&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Preform the acctual copy or move command.&lt;br /&gt;/bin/$action -f $params "${files[@]}" "$dest" 2&gt;/tmp/.error &amp;&lt;br /&gt;&lt;br /&gt;# Start with a empty progress bar.&lt;br /&gt;echo -en "[&gt; 0%]\b\b\b\b\b"&lt;br /&gt;sleep 1&lt;br /&gt;&lt;br /&gt;# If there was an error echo it out and exit.&lt;br /&gt;if [ -s /tmp/.error ] ; then&lt;br /&gt;        cat /tmp/.error&lt;br /&gt; rm /tmp/.error&lt;br /&gt;        exit 1&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# If the source files were empty, just skip to 100% and exit.&lt;br /&gt;if [ $orig_size -eq 0 ] ; then&lt;br /&gt; echo -e "#&gt;100%]\b\b\b\b\b\b"&lt;br /&gt; exit 0&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# While still copying, update the progress bar.&lt;br /&gt;while [ $orig_size -gt $dest_size ] ; do&lt;br /&gt; if [ $dest_dir -eq 1 ] ; then&lt;br /&gt;  # If this was a single directory operation, check the new directories contents.&lt;br /&gt;  dest_size=`cd "$dest" &amp;&amp; du -s "${ofiles[@]}" 2&gt;/dev/null | awk 'BEGIN{total=0}{total+=$1}END{print total+4}'`&lt;br /&gt;  dest_count=`cd "$dest" &amp;&amp; find "${ofiles[@]}" 2&gt;/dev/null | awk 'BEGIN{total=0}{total++}END{print total}'`&lt;br /&gt;        elif [ -f "$dest" ] ; then&lt;br /&gt;  # If this was a single file operation, check the new file.&lt;br /&gt;  dest_size=`du -s "$dest" | awk 'BEGIN{total=0}{total+=$1}END{print total}'`&lt;br /&gt;  dest_count=`find "$dest" | awk 'BEGIN{total=0}{total++}END{print total}'`&lt;br /&gt;        elif [ -d "$dest" ] ; then&lt;br /&gt;  # If this was a many to one directory operation, check for the source items in the new directory.&lt;br /&gt;  dest_size=`cd "$dest" &amp;&amp; du -s "${files[@]}" 2&gt;/dev/null | awk 'BEGIN{total=0}{total+=$1}END{print total}'`&lt;br /&gt;  dest_count=`cd "$dest" &amp;&amp; find "${files[@]}" 2&gt;/dev/null | awk 'BEGIN{total=0}{total++}END{print total}'`&lt;br /&gt;        fi&lt;br /&gt; &lt;br /&gt; # Calculate the operation percentage.&lt;br /&gt;        pct=$((( 100 * $dest_size ) / $orig_size ))&lt;br /&gt; &lt;br /&gt; # Move the the start of the line and clear it.&lt;br /&gt; echo -en "\033[120D\033[K["&lt;br /&gt; # For 0 to 100&lt;br /&gt; for count in `seq -s " " 0 50`; do&lt;br /&gt;  if [ $count -eq $(($pct / 2)) ] ; then&lt;br /&gt;   # Print a '&gt;' at the current percent.&lt;br /&gt;   echo -ne "\033[s&gt;"&lt;br /&gt;  elif [ $count -lt $(($pct / 2)) ] ; then&lt;br /&gt;   # Print a '#' for compleated area.&lt;br /&gt;   echo -n "#"&lt;br /&gt;  else&lt;br /&gt;   # Print a ' ' for space not done.&lt;br /&gt;   echo -n " "&lt;br /&gt;  fi&lt;br /&gt; done&lt;br /&gt; # Print the percentage and restore the cursor to the '&gt;' icon.&lt;br /&gt; echo -en "$pct%]\033[u"&lt;br /&gt;&lt;br /&gt; # If detail was set, show extra info.&lt;br /&gt;        if [ $detail -eq 1 -a  $quiet -eq 0 ] ; then&lt;br /&gt;                echo -en "\033[s"&lt;br /&gt;                echo -en "\033[1A\033[120D\033[K"&lt;br /&gt;                echo -en $message $dest_count/$orig_count files $dest_size/$orig_size bytes to $dest.&lt;br /&gt;                echo -en "\033[u"&lt;br /&gt;        fi&lt;br /&gt; # Wait 1 second.&lt;br /&gt;        sleep 1&lt;br /&gt;done&lt;br /&gt;echo&lt;br /&gt;&lt;br /&gt;exit 0&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-7411228274382753054?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7411228274382753054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=7411228274382753054&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7411228274382753054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7411228274382753054'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/02/fancy-movecopy-commands.html' title='Fancy move/copy commands'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3801260840607835590</id><published>2009-01-19T16:23:00.008-05:00</published><updated>2009-08-18T09:44:38.703-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex Onscreen Keyboard</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Upadte- 8/18/2009 9:31 AM&lt;/span&gt;&lt;br /&gt;Added &lt;a href="http://pynej.dnsalias.com/Web/OnscreenKeyboard/"&gt;Sample&lt;/a&gt;.&lt;br /&gt;Added a fix for the enter key in text areas and for editable DateFields.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Upadte- 1/20/2009 2:49 PM&lt;/span&gt;&lt;br /&gt;Added logic to show or hide the keyboard automatically based on whether a input box is selected.  This can be overridden by setting auto to false.  Also clicking show/hide will toggle this feature.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;This component adds a simple Onscreen Keyboard in flex.  The keyboard is very customizable built at run time from a provided array.  &lt;br /&gt;&lt;br /&gt;It supports buttons to insert text as well as the special actions listed below and custom events and methods.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;Button Types:&lt;/span&gt;&lt;br /&gt;SHIFT        Toggle the shift state.&lt;br /&gt;TAB          Tab to the next control on screen, or previous if shift is down.&lt;br /&gt;ENTER        Simulate an Enter action, causes validation/submit action.&lt;br /&gt;HIDE         Hide the onscreen keyboard.&lt;br /&gt;BACKSPACE    Delete the selected text or one character backward, or forward on shift.&lt;br /&gt;MODE         Switch to the next keyboard layout, or previous on shift.&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;Sample:&lt;/span&gt;&lt;br /&gt;private const sampleKeyMap:Object = [[&lt;br /&gt;    {key: 'a', altkey: 'A'},&lt;br /&gt;    {key: 'b', altkey: 'B'},&lt;br /&gt;    {key: 'c', altkey: 'C'},&lt;br /&gt;    {key: ' ', label: 'Space', span: 2}&lt;br /&gt;], [&lt;br /&gt;    {key: '', label: 'Backspace', altlabel: 'Delete', span: 3, type: OnscreenKeyboard.BACKSPACE}&lt;br /&gt;    {label: 'Shift', span: 2, type: OnscreenKeyboard.SHIFT},&lt;br /&gt;    {label: 'myFucntion', method: myFunction},&lt;br /&gt;    {label: 'myEvent', event: 'myEvent'}&lt;br /&gt;]];&lt;br /&gt;&lt;br /&gt;private function myFunction(event:MouseEvent):void&lt;br /&gt;{&lt;br /&gt;    Alert.Show(osk.shift ? "Shift DOwn" : "Shift Up");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&amp;lt;mx:TextInput/&amp;gt;&lt;br /&gt;&amp;lt;local.OnscreenKeyboard id="osk" modes="{sampleKeyMap}" myEvent="Alert.Show('I got clicked.')"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;OnscreenKeyboard.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt;    import flash.events.Event;&lt;br /&gt;    import flash.events.IEventDispatcher;&lt;br /&gt;    import flash.events.KeyboardEvent;&lt;br /&gt;    import flash.events.MouseEvent;&lt;br /&gt;    import flash.ui.Keyboard;&lt;br /&gt;   &lt;br /&gt;    import mx.binding.utils.BindingUtils;&lt;br /&gt;    import mx.binding.utils.ChangeWatcher;&lt;br /&gt;    import mx.containers.HBox;&lt;br /&gt;    import mx.containers.TitleWindow;&lt;br /&gt;    import mx.containers.VBox;&lt;br /&gt;    import mx.controls.TextArea;&lt;br /&gt;    import mx.controls.TextInput;&lt;br /&gt;    import mx.events.FlexEvent;&lt;br /&gt;    import mx.events.FocusRequestDirection;&lt;br /&gt;    import mx.events.PropertyChangeEvent;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * This is a suctomizable onscreen keyboard.  You can use the provided keymaps of provide your own.&lt;br /&gt;     *&lt;br /&gt;     * A keymap consists of a two dimensional array of keys.  The outer array is a collection of rows and the inner array's are&lt;br /&gt;     *         the keys.  Each key is an object with any of the following properties.&lt;br /&gt;     *&lt;br /&gt;     * keyMap:Object {&lt;br /&gt;     *         key:        Characters to insert on key press.&lt;br /&gt;     *         altkey:     Alternate characters to insert on shift click.&lt;br /&gt;     *         label:      Normal label to display.&lt;br /&gt;     *         altlabel:   Alternate label to display on shift.&lt;br /&gt;     *         span:       Number of button spaces to span over.&lt;br /&gt;     *         method:     Custom method to raise on click. (Can access OnscreenKeyboard.shift to detect shift state.)&lt;br /&gt;     *         event:      Custom event to raise on click. (Can access OnscreenKeyboard.shift to detect shift state.)&lt;br /&gt;     *         type:       Special type of internal action to preform from the following:&lt;br /&gt;     *             SHIFT        Toggle the shift state.&lt;br /&gt;     *             TAB          Tab to the next control on screen, or previous if shift is down.&lt;br /&gt;     *             ENTER        Simulate an Enter action, causes validation/submit action.&lt;br /&gt;     *             HIDE         Hide the onscreen keyboard.&lt;br /&gt;     *             BACKSPACE    Delete the selected text or one character backward, or forward on shift.&lt;br /&gt;     *             MODE         Switch to the next keyboard layout, or previous on shift. &lt;br /&gt;     *&lt;br /&gt;     *&lt;br /&gt;     * Example:&lt;br /&gt;     * private const sampleKeyMap:Object = [[&lt;br /&gt;     *         {key: 'a', altkey: 'A'},&lt;br /&gt;     *         {key: 'b', altkey: 'B'},&lt;br /&gt;     *         {key: 'c', altkey: 'C'},&lt;br /&gt;     *         {key: ' ', label: 'Space', span: 2}&lt;br /&gt;     *     ], [&lt;br /&gt;     *         {key: '', label: 'Backspace', altlabel: 'Delete', span: 3, type: OnscreenKeyboard.BACKSPACE}&lt;br /&gt;     *         {label: 'Shift', span: 2, type: OnscreenKeyboard.SHIFT},&lt;br /&gt;     *         {label: 'myFucntion', method: myFunction},&lt;br /&gt;     *         {label: 'myEvent', event: 'myEvent'}&lt;br /&gt;     *     ]];&lt;br /&gt;     * &lt;br /&gt;     * @author Jeremy Pyne jeremy.pyne@gmail.com&lt;br /&gt;     */&lt;br /&gt;    public class OnscreenKeyboard extends TitleWindow&lt;br /&gt;    {&lt;br /&gt;        /**&lt;br /&gt;         * Create a new instance of OnscreenKeyboard.&lt;br /&gt;         */&lt;br /&gt;        public function OnscreenKeyboard()&lt;br /&gt;        {&lt;br /&gt;            super();&lt;br /&gt;           &lt;br /&gt;            // Set up panel spaceing.&lt;br /&gt;            setStyle("paddingBottom", 6);&lt;br /&gt;            setStyle("paddingLeft", 6);&lt;br /&gt;            setStyle("paddingRight", 6);&lt;br /&gt;           &lt;br /&gt;            // Set default modes.&lt;br /&gt;            modes = [alphaKeyMap, qwertyKeyMap];&lt;br /&gt;           &lt;br /&gt;            // Listen for creation complete event.&lt;br /&gt;            addEventListener(FlexEvent.CREATION_COMPLETE, onInit);&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Width of a one span button. A two span button would be 2x keyWidth + keyGap.&lt;br /&gt;         */&lt;br /&gt;        public var keyWidth:Number = 40;&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Horizontal gap between buttons.&lt;br /&gt;         */&lt;br /&gt;        public var keyGap:Number = 8;&lt;br /&gt;        &lt;br /&gt;        /**&lt;br /&gt;         * Auto show/hide the keyboard.&lt;br /&gt;         */&lt;br /&gt;        public var auto:Boolean = true;&lt;br /&gt;  &lt;br /&gt;        /**&lt;br /&gt;         * Keyboard visibility state.&lt;br /&gt;         */&lt;br /&gt;        private var _visible:Boolean = false;&lt;br /&gt;       &lt;br /&gt;        [Bindable("visibleChanged")]&lt;br /&gt;        /**&lt;br /&gt;         * Is the keyboard currently visible.&lt;br /&gt;         */&lt;br /&gt;        public function get isVisible():Boolean&lt;br /&gt;        {&lt;br /&gt;            return _visible;&lt;br /&gt;        }&lt;br /&gt;        public function set isVisible(value:Boolean):void&lt;br /&gt;        {&lt;br /&gt;            _visible = value;&lt;br /&gt;           &lt;br /&gt;            dispatchEvent(new Event("visibleChanged"));&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        [Bindable("visibleChanged")]&lt;br /&gt;        /**&lt;br /&gt;         * Is the keyboard currently hidden?&lt;br /&gt;         */&lt;br /&gt;        public function get notVisible():Boolean&lt;br /&gt;        {&lt;br /&gt;            return !_visible;&lt;br /&gt;        }&lt;br /&gt;        public function set notVisible(value:Boolean):void&lt;br /&gt;        {&lt;br /&gt;            _visible = !value;&lt;br /&gt;           &lt;br /&gt;            dispatchEvent(new Event("visibleChanged"));&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        [Bindable]&lt;br /&gt;        /**&lt;br /&gt;         * Is the shift button pressed?&lt;br /&gt;         */&lt;br /&gt;        public var shift:Boolean = false;&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Next keyboard to load from modes.&lt;br /&gt;         */&lt;br /&gt;        private var nextMode:Number = -1;&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Array of keyboard layouts.  This can be changed to use cutsom layouts.&lt;br /&gt;         */&lt;br /&gt;        public var modes:Array = new Array();&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Rendered keymaps for displaying onscreen and storing when switched out.&lt;br /&gt;         */&lt;br /&gt;        private var keyMaps:Array = new Array();&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Keyboard portion of the panel.&lt;br /&gt;         */&lt;br /&gt;        private var showView:VBox;&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Hidden version of the keyboard with a show button.&lt;br /&gt;         */&lt;br /&gt;        private var hideView:HBox;&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Shift button for toggleing the shift state.&lt;br /&gt;         */&lt;br /&gt;        public static const SHIFT:String = "shift";&lt;br /&gt;        /**&lt;br /&gt;         * Tab button to go to the next control.&lt;br /&gt;         */&lt;br /&gt;        public static const TAB:String = "tab";&lt;br /&gt;        /**&lt;br /&gt;         * Enter button to validate and submit a form.&lt;br /&gt;         */&lt;br /&gt;        public static const ENTER:String = "enter";&lt;br /&gt;        /**&lt;br /&gt;         * Hide button to hide the keyboard.&lt;br /&gt;         */&lt;br /&gt;        public static const HIDE:String = "hide";&lt;br /&gt;        /**&lt;br /&gt;         * Backspace button to delete the selected text or a charecter.&lt;br /&gt;         */&lt;br /&gt;        public static const BACKSPACE:String = "backspace";&lt;br /&gt;        /**&lt;br /&gt;         * Mode button to switch keyboard layouts.&lt;br /&gt;         */&lt;br /&gt;        public static const MODE:String = "mode";&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Run on creation compleate and load the default keymap.&lt;br /&gt;         */&lt;br /&gt;        private function onInit(event:FlexEvent):void&lt;br /&gt;        {&lt;br /&gt;            // Load the default keymap.&lt;br /&gt;            loadKeyMap();&lt;br /&gt;            &lt;br /&gt;            // Listen for focus change event.&lt;br /&gt;            parentApplication.addEventListener(FocusEvent.FOCUS_IN, focusIn);&lt;br /&gt;            parentApplication.addEventListener(FocusEvent.FOCUS_OUT, focusOut);&lt;br /&gt;&lt;br /&gt;            // Set the initial keyboard state.&lt;br /&gt;            isVisible = focusManager.getFocus() is mx.controls.TextInput || focusManager.getFocus() is mx.controls.TextArea;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Raised when a object gains focus.&lt;br /&gt;         */&lt;br /&gt;        private function focusIn(event:FocusEvent):void&lt;br /&gt;        {&lt;br /&gt;            // If auto is on:&lt;br /&gt;            if(auto) {&lt;br /&gt;                // If a textbox is selected enable the keyobard.&lt;br /&gt;                if(event.target is UITextField)&lt;br /&gt;                    isVisible = true;&lt;br /&gt;                // If not then disable the keyobard.&lt;br /&gt;                else&lt;br /&gt;                    isVisible = false;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        /**&lt;br /&gt;         * Raised when a object loses focus.&lt;br /&gt;         */&lt;br /&gt;        private function focusOut(event:FocusEvent):void&lt;br /&gt;        {&lt;br /&gt;            // If auto is on hide the keyboard.&lt;br /&gt;            if(auto)&lt;br /&gt;                isVisible = false;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        /**&lt;br /&gt;         * Activate a new keymap.  If it hasn't been loaded yet load it.&lt;br /&gt;         */&lt;br /&gt;        private function loadKeyMap():void&lt;br /&gt;        {&lt;br /&gt;            if(!shift) {&lt;br /&gt;                // Switch to the next mode.&lt;br /&gt;                nextMode = nextMode + 1 == modes.length ? 0 : nextMode + 1&lt;br /&gt;            } else {&lt;br /&gt;                // Switch to the prevoise mode.&lt;br /&gt;                nextMode = nextMode == 0 ? modes.length - 1 : nextMode - 1&lt;br /&gt;            }&lt;br /&gt;           &lt;br /&gt;            // If this modes keymap isnt loaded, build it.&lt;br /&gt;            if(!keyMaps[nextMode])&lt;br /&gt;            {&lt;br /&gt;                // Prepare the new keymap.&lt;br /&gt;                keyMaps[nextMode] = new Array();&lt;br /&gt;               &lt;br /&gt;                // For each row of keys.&lt;br /&gt;                for each (var rowMap:Object in modes[nextMode])&lt;br /&gt;                {&lt;br /&gt;                    // Create a new row.&lt;br /&gt;                    var row:HBox = new HBox();&lt;br /&gt;                    // Set a custom horazontal gap.&lt;br /&gt;                    row.setStyle("horizontalGap", keyGap);&lt;br /&gt;                   &lt;br /&gt;                    // For each key in the row.&lt;br /&gt;                    for each (var keyData:Object in rowMap)&lt;br /&gt;                    {&lt;br /&gt;                        // Create a new key.&lt;br /&gt;                        var key:Button = new Button();&lt;br /&gt;                        // Listen for the key press and call key_clickHandler or a custom method.&lt;br /&gt;                        key.addEventListener(MouseEvent.CLICK, keyData.event ? keyData.event : key_clickHandler);&lt;br /&gt;                       &lt;br /&gt;                        /**&lt;br /&gt;                         * This extra property and shit variable is created so that all keys will automaticly detect shift changes and update&lt;br /&gt;                         *         their labels without having to loop through them manualy.&lt;br /&gt;                         */&lt;br /&gt;                        // Bind this keys shift state to the panel.&lt;br /&gt;                        BindingUtils.bindProperty(key, "shift", this, "shift");&lt;br /&gt;                        // Watch for shift changes and fire off key_shiftListener to udate the keys label.&lt;br /&gt;                        ChangeWatcher.watch(key, "shift", key_shiftListener);&lt;br /&gt;                       &lt;br /&gt;                        // Set the initial label to an unshifted state.&lt;br /&gt;                        key.label = keyData.label ? keyData.label : keyData.key;&lt;br /&gt;                       &lt;br /&gt;                        // Set the buttons width to (span * keyWidth) + (keyGap * keyWidth - 1).&lt;br /&gt;                        key.width = keyData.span? keyData.span * (keyWidth + keyGap) - keyGap : keyWidth;&lt;br /&gt;                       &lt;br /&gt;                        // If this button is a toggle button, set toggle.&lt;br /&gt;                        key.toggle = keyData.toggle;&lt;br /&gt;                       &lt;br /&gt;                        // Store all the data for this key in it's data field for later use.&lt;br /&gt;                        key.data = keyData;&lt;br /&gt;                       &lt;br /&gt;                        // Disable focus on this key so that it won't steal focus.&lt;br /&gt;                        key.focusEnabled = false;&lt;br /&gt;                       &lt;br /&gt;                        // If this is a special shift key.&lt;br /&gt;                        if(keyData.type == OnscreenKeyboard.SHIFT) {&lt;br /&gt;                            // Bind the selected field to the shift state.&lt;br /&gt;                            BindingUtils.bindProperty(key, "selected", this, "shift");&lt;br /&gt;                            // Force the key to a toggle key.&lt;br /&gt;                            key.toggle = true;&lt;br /&gt;                        }&lt;br /&gt;                       &lt;br /&gt;                        // Add this eky to the row.&lt;br /&gt;                        row.addChild(key);&lt;br /&gt;                    }&lt;br /&gt;                   &lt;br /&gt;                    // Add the row to the key map.&lt;br /&gt;                    keyMaps[nextMode].push(row);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;           &lt;br /&gt;            // Clear the old rows of keys.&lt;br /&gt;            showView.removeAllChildren();&lt;br /&gt;           &lt;br /&gt;            // oop through each row of keys.&lt;br /&gt;            for each(var rowBox:HBox in keyMaps[nextMode])&lt;br /&gt;                // Add this row to the keyboard view.&lt;br /&gt;                showView.addChild(rowBox);&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Override the createChildren method&lt;br /&gt;         */&lt;br /&gt;        override protected function createChildren():void&lt;br /&gt;        {&lt;br /&gt;            super.createChildren();&lt;br /&gt;           &lt;br /&gt;            // Prepare the main keyboard view.&lt;br /&gt;            if(!showView) {&lt;br /&gt;                // Create a VBox for the rows.&lt;br /&gt;                showView = new VBox();&lt;br /&gt;               &lt;br /&gt;                // Bind the visibility to the isVisible.&lt;br /&gt;                BindingUtils.bindProperty(showView, "visible", this, "isVisible");&lt;br /&gt;                BindingUtils.bindProperty(showView, "includeInLayout", this, "isVisible");&lt;br /&gt;               &lt;br /&gt;                // Add the main view to the panel.&lt;br /&gt;                this.addChild(showView);&lt;br /&gt;            }&lt;br /&gt;           &lt;br /&gt;            // Prepare the collapsed view.&lt;br /&gt;            if(!hideView) {&lt;br /&gt;                // Create a HBox for teh collapsed buttons.&lt;br /&gt;                hideView = new HBox();&lt;br /&gt;               &lt;br /&gt;                // Bind the visibility to the notVisible.&lt;br /&gt;                BindingUtils.bindProperty(hideView, "visible", this, "notVisible");&lt;br /&gt;                BindingUtils.bindProperty(hideView, "includeInLayout", this, "notVisible");&lt;br /&gt;               &lt;br /&gt;                // Create a show button.&lt;br /&gt;                var show:Button = new Button()&lt;br /&gt;               &lt;br /&gt;                // Set the button label.&lt;br /&gt;                show.label = "Show Keyboard";&lt;br /&gt;               &lt;br /&gt;                // Disable the focus for the button so it doesn't steal focus.&lt;br /&gt;                show.focusEnabled = false;&lt;br /&gt;               &lt;br /&gt;                // Listen for the show button to be clicked.&lt;br /&gt;                show.addEventListener(MouseEvent.CLICK, show_clickHandler);&lt;br /&gt;               &lt;br /&gt;                // Add the show button to the HBox.&lt;br /&gt;                hideView.addChild(show);&lt;br /&gt;               &lt;br /&gt;                // Add the collapsed view to the panel.&lt;br /&gt;                this.addChild(hideView);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Fires then the show button is clicked.&lt;br /&gt;         */&lt;br /&gt;        private function show_clickHandler(event:MouseEvent):void&lt;br /&gt;        {&lt;br /&gt;            // Set the isVisible to true.&lt;br /&gt;            isVisible = true;&lt;br /&gt;            // Toggle the autohide mode.&lt;br /&gt;            auto = !auto;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Fires when a key is perssed unless said key has a custom key handler.&lt;br /&gt;         */&lt;br /&gt;        private function key_clickHandler(event:MouseEvent):void&lt;br /&gt;        {&lt;br /&gt;            // Custom shift action.&lt;br /&gt;            if(event.currentTarget.data.type == OnscreenKeyboard.SHIFT) {&lt;br /&gt;                // Toggle shift state.&lt;br /&gt;                shift = !shift;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;            // Custom hide keyboard action.&lt;br /&gt;            if(event.currentTarget.data.type == OnscreenKeyboard.HIDE) {&lt;br /&gt;                // Switch shift off and hide the keyboard.&lt;br /&gt;                isVisible = shift = false;&lt;br /&gt;                // Toggle the autohide mode.&lt;br /&gt;                auto = !auto;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;            // Custom enter action.&lt;br /&gt;            if(event.currentTarget.data.type == OnscreenKeyboard.ENTER) {&lt;br /&gt;                if(focusManager.getFocus() is mx.controls.TextArea) {&lt;br /&gt;                    &lt;br /&gt;                } else {&lt;br /&gt;                    // If there if a IEventDispatcher selected.&lt;br /&gt;                    if(focusManager.getFocus() is  IEventDispatcher) {&lt;br /&gt;                        // Pass it a keyDown and keyUp event.&lt;br /&gt;                        (focusManager.getFocus()as IEventDispatcher).dispatchEvent(new KeyboardEvent("keyDown", true, false, Keyboard.ENTER, Keyboard.ENTER, 0, false, false, shift));&lt;br /&gt;                        (focusManager.getFocus()as IEventDispatcher).dispatchEvent(new KeyboardEvent("keyUp", true, false, Keyboard.ENTER, Keyboard.ENTER, 0, false, false, shift));&lt;br /&gt;                    }&lt;br /&gt;               &lt;br /&gt;                    // Switch shift off.&lt;br /&gt;                    shift = false;&lt;br /&gt;                    return;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            if(event.currentTarget.data.type == OnscreenKeyboard.TAB) {&lt;br /&gt;                // If there if a IEventDispatcher selected.&lt;br /&gt;                if(focusManager.getFocus() is  IEventDispatcher) {&lt;br /&gt;                    focusManager.moveFocus(shift ? FocusRequestDirection.BACKWARD : FocusRequestDirection.FORWARD);&lt;br /&gt;                }&lt;br /&gt;               &lt;br /&gt;                // Switch shift off.&lt;br /&gt;                shift = false;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;            // Custom switch mode action.&lt;br /&gt;            if(event.currentTarget.data.type == OnscreenKeyboard.MODE) {&lt;br /&gt;                // Load the next keymap.&lt;br /&gt;                loadKeyMap();&lt;br /&gt;               &lt;br /&gt;                // Switch shift off.&lt;br /&gt;                shift = false;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;            // Fire a custom event.&lt;br /&gt;            if(event.currentTarget.data.event) {&lt;br /&gt;                // Fire the custom event.&lt;br /&gt;                dispatchEvent(new Event(event.currentTarget.data.event));&lt;br /&gt;               &lt;br /&gt;                // Switch shift off.&lt;br /&gt;                shift = false;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;           &lt;br /&gt;            // Set the key charecter to altkey if shift is set or key.&lt;br /&gt;            var char:String = shift &amp;amp;&amp;amp; event.currentTarget.data.altkey ? event.currentTarget.data.altkey : event.currentTarget.data.key;&lt;br /&gt;            var stepLeft:Boolean = false;&lt;br /&gt;            var stepRight:Boolean = false;&lt;br /&gt;           &lt;br /&gt;            // If a TextInput has focus.&lt;br /&gt;            if(focusManager.getFocus() is mx.controls.TextInput) {&lt;br /&gt;                // Get the focused control.&lt;br /&gt;                var tiControl:mx.controls.TextInput = (focusManager.getFocus() as mx.controls.TextInput);&lt;br /&gt;                stepLeft = event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE &amp;amp;&amp;amp; tiControl.selectionBeginIndex == tiControl.selectionEndIndex &amp;amp;&amp;amp; shift == false;&lt;br /&gt;                stepRight = event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE &amp;amp;&amp;amp; tiControl.selectionBeginIndex == tiControl.selectionEndIndex &amp;amp;&amp;amp; shift == true;&lt;br /&gt;               &lt;br /&gt;                // Replace the selected text with the charecter.  If step then replace an extra charecter.&lt;br /&gt;                tiControl.text = tiControl.text.substr(0, tiControl.selectionBeginIndex - (stepLeft ? 1 : 0)) + char + tiControl.text.substr(tiControl.selectionEndIndex + (stepRight ? 1 : 0), tiControl.text.length - tiControl.selectionEndIndex)&lt;br /&gt;                // Reposition the cursor to the right the length of the charecters inserted or adjust for a backspace or delete.&lt;br /&gt;                tiControl.selectionBeginIndex = tiControl.selectionEndIndex = tiControl.selectionBeginIndex + char.length - (event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE ? (stepLeft ? 1 : 0)  : -1);&lt;br /&gt;            }&lt;br /&gt;            // If a TextArea has focus.&lt;br /&gt;            if(focusManager.getFocus() is mx.controls.TextArea) {&lt;br /&gt;                // Get the focused control.&lt;br /&gt;                var taControl:mx.controls.TextArea = (focusManager.getFocus() as mx.controls.TextArea);&lt;br /&gt;                stepLeft = event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE &amp;amp;&amp;amp; taControl.selectionBeginIndex == taControl.selectionEndIndex &amp;amp;&amp;amp; shift == false;&lt;br /&gt;                stepRight = event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE &amp;amp;&amp;amp; taControl.selectionBeginIndex == taControl.selectionEndIndex &amp;amp;&amp;amp; shift == true;&lt;br /&gt;               &lt;br /&gt;                // Replace the selected text with the charecter.  If step then replace an extra charecter.&lt;br /&gt;                taControl.text = taControl.text.substr(0, taControl.selectionBeginIndex - (stepLeft ? 1 : 0)) + char + taControl.text.substr(taControl.selectionEndIndex + (stepRight ? 1 : 0), taControl.text.length - taControl.selectionEndIndex)&lt;br /&gt;                // Reposition the cursor to the right the length of the charecters inserted or adjust for a backspace or delete.&lt;br /&gt;                taControl.selectionBeginIndex = taControl.selectionEndIndex = taControl.selectionBeginIndex + char.length - (event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE ? (stepLeft ? 1 : 0)  : -1);&lt;br /&gt;            }&lt;br /&gt;            // If a DateField has focus&lt;br /&gt;            if(focusManager.getFocus() is mx.controls.DateField) {&lt;br /&gt;                // Get the focused control.&lt;br /&gt;                var tdControl:mx.controls.DateField = (focusManager.getFocus() as mx.controls.DateField);&lt;br /&gt;                if(tdControl.editable) {&lt;br /&gt;                    if(event.currentTarget.data.type == OnscreenKeyboard.BACKSPACE)&lt;br /&gt;                        // Can't access selected text so jsut delete last letter. &lt;br /&gt;                        tdControl.text = tdControl.text.substr(0, tdControl.text.length - 1);&lt;br /&gt;                    else&lt;br /&gt;                        // Can't access selected text so just appent to text.&lt;br /&gt;                        tdControl.text = tdControl.text + char;&lt;br /&gt;&lt;br /&gt;                    // Fire off a chaneg event on the source control.&lt;br /&gt;                    tdControl.dispatchEvent(new Event(Event.CHANGE, false, false));&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;           &lt;br /&gt;            // Switch shift off.&lt;br /&gt;            shift = false;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Fires when the shift state has changed on all buttons.&lt;br /&gt;         */&lt;br /&gt;        private function key_shiftListener(event:PropertyChangeEvent):void&lt;br /&gt;        {&lt;br /&gt;            //If shift is clicked use altlabel or the altkey.&lt;br /&gt;            if(shift &amp;amp;&amp;amp; event.source.data.altlabel)&lt;br /&gt;                event.source.label = event.source.data.altlabel;&lt;br /&gt;            else if(shift &amp;amp;&amp;amp; event.source.data.altkey)&lt;br /&gt;                event.source.label = event.source.data.altkey;&lt;br /&gt;            // If neither of these exist or shift isn't down then use the label or the key.&lt;br /&gt;            else if(event.source.data.label)&lt;br /&gt;                event.source.label = event.source.data.label;&lt;br /&gt;            else&lt;br /&gt;                event.source.label = event.source.data.key;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        /**&lt;br /&gt;         * Default alphabetical keyboard layout.&lt;br /&gt;         */&lt;br /&gt;        private const alphaKeyMap:Object = [[&lt;br /&gt;                {key: '1', altkey: '!'},&lt;br /&gt;                {key: '2', altkey: '@'},&lt;br /&gt;                {key: '3', altkey: '#'},&lt;br /&gt;                {key: '4', altkey: '$'},&lt;br /&gt;                {key: '5', altkey: '%'},&lt;br /&gt;                {key: '6', altkey: '^'},&lt;br /&gt;                {key: '7', altkey: '&amp;amp;'},&lt;br /&gt;                {key: '8', altkey: '*'},&lt;br /&gt;                {key: '9', altkey: '('},&lt;br /&gt;                {key: '0', altkey: ')'},&lt;br /&gt;                {key: '-', altkey: '_'},&lt;br /&gt;                {key: '=', altkey: '+'},&lt;br /&gt;                {key: '', label: 'Backspace', altlabel: 'Delete', span: 3, type: OnscreenKeyboard.BACKSPACE}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 'a', altkey: 'A'},&lt;br /&gt;                {key: 'b', altkey: 'B'},&lt;br /&gt;                {key: 'c', altkey: 'C'},&lt;br /&gt;                {key: 'd', altkey: 'D'},&lt;br /&gt;                {key: 'e', altkey: 'E'},&lt;br /&gt;                {key: 'f', altkey: 'F'},&lt;br /&gt;                {key: 'g', altkey: 'G'},&lt;br /&gt;                {key: 'h', altkey: 'H'},&lt;br /&gt;                {key: 'i', altkey: 'I'},&lt;br /&gt;                {key: 'j', altkey: 'J'},&lt;br /&gt;                {key: '[', altkey: '{'},&lt;br /&gt;                {key: ']', altkey: '}'},&lt;br /&gt;                {key: '\\', altkey: '|'},&lt;br /&gt;                {label: 'Tab', span: 2, type: OnscreenKeyboard.TAB},&lt;br /&gt;                {label: 'Enter', span: 2, type: OnscreenKeyboard.ENTER}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 'k', altkey: 'K'},&lt;br /&gt;                {key: 'l', altkey: 'L'},&lt;br /&gt;                {key: 'm', altkey: 'M'},&lt;br /&gt;                {key: 'n', altkey: 'N'},&lt;br /&gt;                {key: 'o', altkey: 'O'},&lt;br /&gt;                {key: 'p', altkey: 'P'},&lt;br /&gt;                {key: 'q', altkey: 'Q'},&lt;br /&gt;                {key: 'r', altkey: 'R'},&lt;br /&gt;                {key: 's', altkey: 'S'},&lt;br /&gt;                {key: ';', altkey: ':'},&lt;br /&gt;                {key: "'", altkey: '"'},&lt;br /&gt;                {key: ' ', label: 'Space', span: 2},&lt;br /&gt;                {label: 'Shift', span: 2, type: OnscreenKeyboard.SHIFT}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 't', altkey: 'T'},&lt;br /&gt;                {key: 'u', altkey: 'U'},&lt;br /&gt;                {key: 'v', altkey: 'V'},&lt;br /&gt;                {key: 'w', altkey: 'W'},&lt;br /&gt;                {key: 'x', altkey: 'X'},&lt;br /&gt;                {key: 'y', altkey: 'Y'},&lt;br /&gt;                {key: 'z', altkey: 'Z'},&lt;br /&gt;                {key: ',', altkey: '&lt;'},&lt;br /&gt;                {key: '.', altkey: '&gt;'},&lt;br /&gt;                {key: '/', altkey: '?'},&lt;br /&gt;                {key: '`', altkey: '~'},&lt;br /&gt;                {label: 'Mode', span: 2, type: OnscreenKeyboard.MODE},&lt;br /&gt;                {label: 'Hide', span: 2, type: OnscreenKeyboard.HIDE}&lt;br /&gt;            ]];&lt;br /&gt;           &lt;br /&gt;            /**&lt;br /&gt;             * Default qwerty keyboard layout.&lt;br /&gt;             */&lt;br /&gt;            private const qwertyKeyMap:Object = [[&lt;br /&gt;                {key: '1', altkey: '!'},&lt;br /&gt;                {key: '2', altkey: '@'},&lt;br /&gt;                {key: '3', altkey: '#'},&lt;br /&gt;                {key: '4', altkey: '$'},&lt;br /&gt;                {key: '5', altkey: '%'},&lt;br /&gt;                {key: '6', altkey: '^'},&lt;br /&gt;                {key: '7', altkey: '&amp;amp;'},&lt;br /&gt;                {key: '8', altkey: '*'},&lt;br /&gt;                {key: '9', altkey: '('},&lt;br /&gt;                {key: '0', altkey: ')'},&lt;br /&gt;                {key: '-', altkey: '_'},&lt;br /&gt;                {key: '=', altkey: '+'},&lt;br /&gt;                {key: '', label: 'Backspace', altlabel: 'Delete', span: 3, type: OnscreenKeyboard.BACKSPACE}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 'q', altkey: 'Q'},&lt;br /&gt;                {key: 'w', altkey: 'W'},&lt;br /&gt;                {key: 'e', altkey: 'E'},&lt;br /&gt;                {key: 'r', altkey: 'R'},&lt;br /&gt;                {key: 't', altkey: 'T'},&lt;br /&gt;                {key: 'y', altkey: 'Y'},&lt;br /&gt;                {key: 'u', altkey: 'U'},&lt;br /&gt;                {key: 'i', altkey: 'I'},&lt;br /&gt;                {key: 'o', altkey: 'O'},&lt;br /&gt;                {key: 'p', altkey: 'P'},&lt;br /&gt;                {key: '[', altkey: '{'},&lt;br /&gt;                {key: ']', altkey: '}'},&lt;br /&gt;                {key: '\\', altkey: '|'},&lt;br /&gt;                {label: 'Tab', span: 2, type: OnscreenKeyboard.TAB},&lt;br /&gt;                {label: 'Enter', span: 2, type: OnscreenKeyboard.ENTER}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 'a', altkey: 'A'},&lt;br /&gt;                {key: 's', altkey: 'S'},&lt;br /&gt;                {key: 'd', altkey: 'D'},&lt;br /&gt;                {key: 'f', altkey: 'F'},&lt;br /&gt;                {key: 'g', altkey: 'G'},&lt;br /&gt;                {key: 'h', altkey: 'H'},&lt;br /&gt;                {key: 'j', altkey: 'J'},&lt;br /&gt;                {key: 'k', altkey: 'K'},&lt;br /&gt;                {key: 'l', altkey: 'L'},&lt;br /&gt;                {key: ';', altkey: ':'},&lt;br /&gt;                {key: "'", altkey: '"'},&lt;br /&gt;                {key: ' ', label: 'Space', span: 2},&lt;br /&gt;                {label: 'Shift', span: 2, type: OnscreenKeyboard.SHIFT}&lt;br /&gt;            ], [&lt;br /&gt;                {key: 'z', altkey: 'Z'},&lt;br /&gt;                {key: 'x', altkey: 'X'},&lt;br /&gt;                {key: 'c', altkey: 'C'},&lt;br /&gt;                {key: 'v', altkey: 'V'},&lt;br /&gt;                {key: 'b', altkey: 'B'},&lt;br /&gt;                {key: 'n', altkey: 'N'},&lt;br /&gt;                {key: 'm', altkey: 'M'},&lt;br /&gt;                {key: ',', altkey: '&lt;'},&lt;br /&gt;                {key: '.', altkey: '&gt;'},&lt;br /&gt;                {key: '/', altkey: '?'},&lt;br /&gt;                {key: '`', altkey: '~'},&lt;br /&gt;                {label: 'Mode', span: 2, type: OnscreenKeyboard.MODE},&lt;br /&gt;                {label: 'Hide', span: 2, type: OnscreenKeyboard.HIDE}&lt;br /&gt;            ]];&lt;br /&gt;           &lt;br /&gt;            /**&lt;br /&gt;             * Default number pad layout.&lt;br /&gt;             */&lt;br /&gt;            private const numberKeyMap:Object = [[&lt;br /&gt;                {key: '1'},&lt;br /&gt;                {key: '2'},&lt;br /&gt;                {key: '3'},&lt;br /&gt;                {key: '/'}&lt;br /&gt;            ], [&lt;br /&gt;                {key: '4'},&lt;br /&gt;                {key: '5'},&lt;br /&gt;                {key: '6'},&lt;br /&gt;                {key: '*'}&lt;br /&gt;            ], [&lt;br /&gt;                {key: '7'},&lt;br /&gt;                {key: '8'},&lt;br /&gt;                {key: '9'},&lt;br /&gt;                {key: '-'}&lt;br /&gt;            ], [&lt;br /&gt;                {key: '0', span: 2},&lt;br /&gt;                {key: '.'},&lt;br /&gt;                {key: '+'}&lt;br /&gt;            ], [&lt;br /&gt;                {key: '', label: 'Backspace', altlabel: 'Delete', span: 2, type: OnscreenKeyboard.BACKSPACE},&lt;br /&gt;                {label: 'Enter', span: 2, type: OnscreenKeyboard.ENTER}&lt;br /&gt;            ], [&lt;br /&gt;                {label: 'Mode', span: 2, type: OnscreenKeyboard.MODE},&lt;br /&gt;                {label: 'Hide', span: 2, type: OnscreenKeyboard.HIDE}&lt;br /&gt;            ]];&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;Button.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt; import mx.controls.Button;&lt;br /&gt; /**&lt;br /&gt;  * Custom Button control with an extra shift property.&lt;br /&gt;  *  &lt;br /&gt;  * @author Jeremy Pyne jeremy.pyne@gmail.com&lt;br /&gt;  */ &lt;br /&gt; public class Button extends mx.controls.Button&lt;br /&gt; {&lt;br /&gt;  /**&lt;br /&gt;   * Create an instance of the Button class.&lt;br /&gt;   */&lt;br /&gt;  public function Button()&lt;br /&gt;  {&lt;br /&gt;   super();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  [Bindable]&lt;br /&gt;  public var shift:Boolean = false;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3801260840607835590?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3801260840607835590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3801260840607835590&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3801260840607835590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3801260840607835590'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2009/01/this-component-adds-simple-onscreen.html' title='Flex Onscreen Keyboard'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3677784412985391903</id><published>2008-11-05T14:08:00.003-05:00</published><updated>2008-11-05T14:46:43.634-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Network'/><category scheme='http://www.blogger.com/atom/ns#' term='Astaro'/><title type='text'>My new Network Layout with Astaro</title><content type='html'>So I being a Comcast customer, not by choice, decided I wanted to be able to more closely monitor and control my network usage.  I was toying around with different ways to monitor my traffic but didn't want to replace my router not add another device to the network outside the router. &lt;br /&gt;&lt;br /&gt;I also looked at monitoring traffic per device, but this has its own problems such as missing traffic and counting local traffic twice, once on the source and again on the destination.  The long an the short is I can only do what I want by having the traffic bass through a single device, an &lt;a href="http://www.astaro.com/"&gt;Astaro Security Gateway&lt;/a&gt;  in my case.&lt;br /&gt;&lt;br /&gt;I do run &lt;a href="http://www.vmware.com/products/server/"&gt;VMware Server&lt;/a&gt; on my single server though and desided if I could get a configuration with the ASG running in a virtual machine thus not needing the extra hardware.  While, after a few hours of testing, and restoring my router once, I got it up and working.  Heere is a diagram of the layout and soem notes.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_546vW2tHQGQ/SRHwOQg9pyI/AAAAAAAAAaA/wn7LBal5PSQ/s1600-h/Network+Layout.png"&gt;&lt;img style="margin: 0px auto 10px; cursor: pointer; width: 309px; height: 400px;" src="http://2.bp.blogspot.com/_546vW2tHQGQ/SRHwOQg9pyI/AAAAAAAAAaA/wn7LBal5PSQ/s400/Network+Layout.png" alt="" id="BLOGGER_PHOTO_ID_5265253567114880802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Black lines on white are pyisical network connections.&lt;br /&gt;Black lines on blue are vmware bridge connections.&lt;br /&gt;Green lines indicate internal network traffic.&lt;br /&gt;Red lines indicate external network traffic.&lt;br /&gt;&lt;br /&gt;Some variouse notes on my configuration:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The monitored internal netowrk is on the 192.168.1.* subnet.&lt;/li&gt;&lt;li&gt;The unmonitored external network is on the 192.168.1.150.* subnet.&lt;/li&gt;&lt;li&gt;Both networks exist on the same pyisical switch but the DHCP server doles out only internal addresses.  A computer could be configured staticly to the external subnet and I havn't found a way to prevent this but in oder to circumvent the ASG the user would have to know about the second subnet.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The server has only a single gigabit network interface card on it.  Traffic on the internal network heading out will go to the server and be passed onto the ASG's internal virtual interface.  Next the ASG will proccess the traffic and send it back out on the external interface.  The server will pass the traffic back out on its internal interface but the traffic will really be on the external subnet so it will pass off to the router.&lt;/li&gt;&lt;li&gt;The internal and external networks co-exist on the same network segment just differnet segments, for that reson I pourpasly left on the main routers Firewall and NAT.&lt;/li&gt;&lt;li&gt;There is no noticible impact on network preformance as the traffic is already limited by teh 6M cable modem connection.&lt;/li&gt;&lt;li&gt;I can not monitor just the external interface of teh ASG for trafic usage statistics with &lt;a href="http://humdi.net/vnstat/"&gt;vnstat&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3677784412985391903?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3677784412985391903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3677784412985391903&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3677784412985391903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3677784412985391903'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/11/my-new-network-layout-with-astaro.html' title='My new Network Layout with Astaro'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_546vW2tHQGQ/SRHwOQg9pyI/AAAAAAAAAaA/wn7LBal5PSQ/s72-c/Network+Layout.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2038202953226840177</id><published>2008-11-05T10:22:00.005-05:00</published><updated>2009-02-04T15:13:06.201-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex DateChooser Toggle</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Upadte- 2/4/2009 3:11 PM&lt;/span&gt;&lt;br /&gt;Added support for simulating the shift key as well.  If allowDisjointSelection is set then each click will toggle a date like id did before.&lt;br /&gt;If it switched off though now the first click will set a start date and the second click an end date so that a range can be selected via clicking.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;This component changes the DateChooser class to add a toggle option.  When set clicking on a date will toggle it in the same way that control-clicking does.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;DateChooser.as&lt;/span&gt;&lt;br /&gt;package &lt;br /&gt;{&lt;br /&gt; import flash.events.Event;&lt;br /&gt; import flash.events.MouseEvent;&lt;br /&gt; &lt;br /&gt; import mx.controls.DateChooser;&lt;br /&gt; import mx.core.mx_internal;&lt;br /&gt; &lt;br /&gt; use namespace mx_internal;&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * This custom DateChooser adds the ability to toggle dates on click or to select a range with two clicks instead of using &lt;br /&gt;  *   keyboard modifiers.  If allowDisjointSelection is set then clicking a date will toggle it on or off.  If not then the first click &lt;br /&gt;  *   will set the start date and the second click will set the end date.  In this mode a third click will un-select the first range and &lt;br /&gt;  *   set a new start date.&lt;br /&gt;  * &lt;br /&gt;  * @author Jeremy Pyne jeremy.pyne@gmail.com&lt;br /&gt;  */&lt;br /&gt; public class DateChooser extends mx.controls.DateChooser&lt;br /&gt; {&lt;br /&gt;  public function DateChooser()&lt;br /&gt;  {&lt;br /&gt;   super();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  override protected function createChildren():void&lt;br /&gt;  {&lt;br /&gt;   super.createChildren();&lt;br /&gt;   &lt;br /&gt;   dateGrid.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, 1);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;   //----------------------------------&lt;br /&gt;  //  toggleSelection&lt;br /&gt;  //----------------------------------&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  @private&lt;br /&gt;   *  Storage for the allowMultipleSelection property.&lt;br /&gt;   */&lt;br /&gt;  private var _toggleSelection:Boolean = false;&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  [Bindable("toggleSelectionChanged")]&lt;br /&gt;  [Inspectable(category="General", defaultValue="true")]&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  If &lt;code&gt;true&lt;/code&gt;, specifies that selecting dates will toggle them. Simulates controll key down.&lt;br /&gt;   *&lt;br /&gt;   *  @default false&lt;br /&gt;   *  @helpid&lt;br /&gt;   *  @tiptext Selections are toggled if true&lt;br /&gt;   */&lt;br /&gt;  public function get toggleSelection():Boolean&lt;br /&gt;  {&lt;br /&gt;   return _toggleSelection;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  @private&lt;br /&gt;   */&lt;br /&gt;  public function set toggleSelection(value:Boolean):void&lt;br /&gt;  {&lt;br /&gt;   _toggleSelection = value;&lt;br /&gt;   dispatchEvent(new Event("toggleSelectionChanged"));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;  private function mouseUpHandler(event:MouseEvent):void&lt;br /&gt;  {&lt;br /&gt;   if(toggleSelection &amp;&amp; allowMultipleSelection &amp;&amp; event.shiftKey == false)&lt;br /&gt;   {&lt;br /&gt;    // If allowDisjointSelection is set then simulate the control key pressed.&lt;br /&gt;    if(allowDisjointSelection)&lt;br /&gt;     event.ctrlKey = true;&lt;br /&gt;    // If not and there is something selected.&lt;br /&gt;    else if(this.selectedRanges.length)&lt;br /&gt;     // Simulate the shift key down if the currect selectedRange is that of a single day.&lt;br /&gt;     event.shiftKey = this.selectedRanges[0].rangeStart.time == this.selectedRanges[0].rangeEnd.time;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2038202953226840177?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2038202953226840177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2038202953226840177&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2038202953226840177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2038202953226840177'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/11/flex-datechooser-toggle.html' title='Flex DateChooser Toggle'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1618789181925095717</id><published>2008-10-25T00:05:00.004-04:00</published><updated>2008-10-25T00:12:01.939-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Photography'/><title type='text'>New Styleized Photo's</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_546vW2tHQGQ/SQKbSxcAPcI/AAAAAAAAAZ4/fmy15iWbbbU/s1600-h/IMG_1093.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer; width: 400px; height: 300px;" src="http://4.bp.blogspot.com/_546vW2tHQGQ/SQKbSxcAPcI/AAAAAAAAAZ4/fmy15iWbbbU/s400/IMG_1093.jpg" alt="" id="BLOGGER_PHOTO_ID_5260938061532904898" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I was playing around with some pictures from my trip to Denver last month and posted some new stylized photo's on my flicker page.  Check them out &lt;a href="http://www.flickr.com/photos/pynej/archives/date-posted/2008/10/25/"&gt;here&lt;/a&gt;.  The original photos are also available &lt;a href="http://www.flickr.com/photos/pynej/archives/date-posted/2008/10/07/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1618789181925095717?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1618789181925095717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1618789181925095717&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1618789181925095717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1618789181925095717'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/new-styleized-photos.html' title='New Styleized Photo&apos;s'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_546vW2tHQGQ/SQKbSxcAPcI/AAAAAAAAAZ4/fmy15iWbbbU/s72-c/IMG_1093.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-8785682196449861941</id><published>2008-10-22T16:47:00.005-04:00</published><updated>2008-10-24T14:36:12.725-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Themes'/><category scheme='http://www.blogger.com/atom/ns#' term='Firefox'/><title type='text'>Google Chrome theme for Firefox</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_546vW2tHQGQ/SQIVFti9F9I/AAAAAAAAAZg/13OjRden6HI/s1600-h/Firefox-Theme.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer; width: 400px; height: 310px;" src="http://4.bp.blogspot.com/_546vW2tHQGQ/SQIVFti9F9I/AAAAAAAAAZg/13OjRden6HI/s400/Firefox-Theme.PNG" alt="" id="BLOGGER_PHOTO_ID_5260790502591895506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is how my Firefox looks.  Note that this isn't maximized or trimmed, there just isn't a title of menu bar.  The menu is accessible via a drop down on the top right as well as window controls. &lt;br /&gt;&lt;br /&gt;Here is a list of the addon's and tweaks I used to get this look:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/8782"&gt;Chromifox&lt;/a&gt;: This is the primary theme used.&lt;/li&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2108"&gt;Stylish&lt;/a&gt;: For minor CSS tweaks.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://userstyles.org/styles/10470"&gt;Chromifox Falconer&lt;/a&gt;: Move the tabs to the top of the screen.&lt;/li&gt;&lt;li&gt;&lt;a href="http://userstyles.org/styles/11480"&gt;Chromifox Fixes&lt;/a&gt;: Fix for Zombie mode and minimized tabs.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.krickelkrackel.de/autohide/"&gt;Autohide&lt;/a&gt;: This extension allows you to go into Zombie mode and hide the titlebar.&lt;/li&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/4550"&gt;Compact Menu 2&lt;/a&gt;: This extension allows you to hide the main menu and add a toolbar icon instead.&lt;/li&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/3780"&gt;FaviconizeTab&lt;/a&gt;: Lets you minimize tabs to just show their icon.&lt;/li&gt;&lt;/ul&gt;Extra settings:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;When using Autohide you will need to open up &lt;span style="font-weight: bold;"&gt;about:config&lt;/span&gt; and set the setting &lt;span style="font-weight: bold;"&gt;extensions.autohide.zombieBorder&lt;/span&gt; to &lt;span style="font-weight: bold;"&gt;0&lt;/span&gt;.  This will remove the titlebar and border when you go into zombie mode.  (Shift-F11)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-8785682196449861941?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/8785682196449861941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=8785682196449861941&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8785682196449861941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8785682196449861941'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/google-chrome-theme-for-firefox.html' title='Google Chrome theme for Firefox'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_546vW2tHQGQ/SQIVFti9F9I/AAAAAAAAAZg/13OjRden6HI/s72-c/Firefox-Theme.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-108457655763935819</id><published>2008-10-22T14:12:00.004-04:00</published><updated>2008-10-22T14:37:52.549-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Firefox'/><title type='text'>Runnig Firefox Pre Releases</title><content type='html'>I always like the latest and greatest so here are instructions for running the latest and greatest Firefox builds while maintaining browser auto-updated and extensions.&lt;br /&gt;&lt;br /&gt;First off you need to download the latest night build from &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/"&gt;http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/&lt;/a&gt;.  Alternately you can get the 3.1 Beta witch is not updated as often but still has the newest features at &lt;a href="http://releases.mozilla.org/pub/mozilla.org/firefox/releases/3.1b1/"&gt;http://releases.mozilla.org/pub/mozilla.org/firefox/releases/3.1b1/&lt;/a&gt;.  The second option may be more stable but it will replace your Firefox install where as the nightly installs in a separate location.&lt;br /&gt;&lt;br /&gt;Regardless of witch version you install when done you will have a few problems, namely most ad dons will report out of date and won't load.   To fix this open up Firefox and navigate to &lt;span style="font-weight: bold;"&gt;about:config&lt;/span&gt;.  Now search for the setting &lt;span style="font-weight: bold;"&gt;extensions.checkCompatibility&lt;/span&gt; or add it if needed.  Also set it to &lt;span style="font-weight: bold;"&gt;false&lt;/span&gt;.  This will simply make extensions load even if their versions don't match Firefox.  Now some addon's still may not work but all of my major addon's work just fine.&lt;br /&gt;&lt;br /&gt;Also while here we want to change a few other settings.  Next find the setting &lt;span style="font-weight: bold;"&gt;general.useragent.extra.firefox&lt;/span&gt; and set it to &lt;span style="font-weight: bold;"&gt;Firefox/3.0&lt;/span&gt;.  This just changes the Firefox version reported to web sites so the Install Extension links in &lt;a href="https://addons.mozilla.org/en-US/firefox"&gt;https://addons.mozilla.org/en-US/firefox&lt;/a&gt; still work correctly.&lt;br /&gt;&lt;br /&gt;Also we want to enable the new &lt;a href="http://weblogs.mozillazine.org/roadmap/archives/2008/08/tracemonkey_javascript_lightsp.html"&gt;TraceMonkey Javascript Engine&lt;/a&gt; witch is much faster and one of the best features of 3.1.  To do so again in about:config find the setting &lt;span style="font-weight: bold;"&gt;javascript.options.jit.content&lt;/span&gt; and set it to &lt;span style="font-weight: bold;"&gt;true&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;You should now be all set, just restart Firefox and check out some sites.  Also note that extensions and settings are shared between all versions of Firefox but noting we changed will break Firefox 3.0.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-108457655763935819?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/108457655763935819/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=108457655763935819&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/108457655763935819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/108457655763935819'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/runnig-firefox-pre-releases.html' title='Runnig Firefox Pre Releases'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7131898807154866359</id><published>2008-10-22T11:02:00.003-04:00</published><updated>2009-08-18T09:53:15.355-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubiquity'/><title type='text'>Ubiquity Command: Search Google Web History</title><content type='html'>Search your Google Web History.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;&lt;a target="_blank" href="http://pynej.dnsalias.com/Shared/ubiquity.php?command=web-history"&gt;web-history.js&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;makeSearchCommand({&lt;br /&gt; name: "web-history",&lt;br /&gt; homepage: "http://pynej.blogspot.com/",&lt;br /&gt; author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},&lt;br /&gt; url: "http://www.google.com/history/find?q={QUERY}",&lt;br /&gt; icon: "http://www.google.com/favicon.ico",&lt;br /&gt; description: "Searches Google Web History."&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-7131898807154866359?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7131898807154866359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=7131898807154866359&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7131898807154866359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7131898807154866359'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/ubiquity-command-search-google-web.html' title='Ubiquity Command: Search Google Web History'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-5327422993053205143</id><published>2008-10-22T10:46:00.005-04:00</published><updated>2009-08-18T09:53:05.148-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubiquity'/><title type='text'>Ubiquity Command: About Config</title><content type='html'>Here is a simple command to display the about:config page.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;&lt;a target="_blank" href="http://pynej.dnsalias.com/Shared/ubiquity.php?command=about_config"&gt;about_config.js&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;CmdUtils.CreateCommand({&lt;br /&gt;  name: "about_config",&lt;br /&gt;  homepage: "http://pynej.blogspot.com/",&lt;br /&gt;  author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},&lt;br /&gt;  description: "Open the firefox about:config page.",&lt;br /&gt;  execute: function() {&lt;br /&gt;    Utils.openUrlInBrowser("about:config");&lt;br /&gt;  }&lt;br /&gt;})&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-5327422993053205143?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/5327422993053205143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=5327422993053205143&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5327422993053205143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5327422993053205143'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/ubiquity-command-about-config.html' title='Ubiquity Command: About Config'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-5589317294439188085</id><published>2008-10-20T16:45:00.001-04:00</published><updated>2008-10-20T17:03:27.934-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Firefox'/><title type='text'>My Firefox Extentions</title><content type='html'>Here is how I see the world.&lt;br /&gt;&lt;br /&gt;(Picture coming soon)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1865"&gt;AdBlock Plus&lt;/a&gt;: Kill the Adds.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/26"&gt;Download Statusbar&lt;/a&gt;: Less intrusive file downloads.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1843"&gt;Firebug&lt;/a&gt;: Easy access to view a page source.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/6683"&gt;Firecookie&lt;/a&gt;: Play with cookies.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2464"&gt;FoxProxy&lt;/a&gt;: Intelligent proxies with regex.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://wiki.mozilla.org/Labs/Ubiquity"&gt;Ubiquity&lt;/a&gt;: Keyboard based actions.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;Greasemonkey&lt;/a&gt;: User scripts.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2108"&gt;Stylish&lt;/a&gt;: User Styles&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.arend-von-reinersdorff.com/folders4gmail/"&gt;Folders4Gmail&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.globexdesigns.com/gmail"&gt;Gmail Redesigned&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.globexdesigns.com/gcal"&gt;Gcal Redesigned&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://userstyles.org/styles/2318"&gt;OS X Style Google Reader &lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-5589317294439188085?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/5589317294439188085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=5589317294439188085&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5589317294439188085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5589317294439188085'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/my-firefox-extentions.html' title='My Firefox Extentions'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3669041508018584886</id><published>2008-10-20T16:37:00.004-04:00</published><updated>2009-08-18T09:52:55.067-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubiquity'/><title type='text'>Ubiquity Command: Open in New Tab</title><content type='html'>Here is a Ubiquity script to open a link in a  new tab.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;&lt;a target="_blank" href="http://pynej.dnsalias.com/Shared/ubiquity.php?command=open_in_new_tab"&gt;open_in_new_tab.js&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;CmdUtils.CreateCommand({&lt;br /&gt;  name: "open_in_new_tab",&lt;br /&gt;  homepage: "http://pynej.blogspot.com/",&lt;br /&gt;  author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},&lt;br /&gt;  description: "Open the selected url in a new tab.",&lt;br /&gt;  takes: {"URL": noun_type_url},&lt;br /&gt;  preview: function( pblock, url ) {&lt;br /&gt;    pblock.innerHTML = "Will open: " + url.text;&lt;br /&gt;  },&lt;br /&gt;  execute: function( url ) {&lt;br /&gt;    Utils.openUrlInBrowser(url.text);&lt;br /&gt;  }&lt;br /&gt;})&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3669041508018584886?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3669041508018584886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3669041508018584886&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3669041508018584886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3669041508018584886'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/ubiquity-command-open-in-new-tab.html' title='Ubiquity Command: Open in New Tab'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6172252093573510520</id><published>2008-10-20T13:10:00.009-04:00</published><updated>2009-08-18T09:55:11.029-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubiquity'/><title type='text'>Ubiquity Command: Note in Reader</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Update: Monday, August 18, 2009 9:54 am&lt;/span&gt;&lt;br /&gt;Updated script to work with newer framework.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update: Monday, October 20, 2008 2:52 pm&lt;/span&gt;&lt;br /&gt;Updated the script to actually work.  Also you can now enter your note on the command line itself.&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Here is a Ubiquity script to note a page in Google Reader.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;&lt;a target="_blank" href="http://pynej.dnsalias.com/Shared/ubiquity.php?command=note-in-reader"&gt;note-in-reader.js&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;CmdUtils.CreateCommand({&lt;br /&gt;  name: ["note-in-reader", "add-to-reader", "share-to-reader"],&lt;br /&gt;  homepage: "http://pynej.blogspot.com/",&lt;br /&gt;  author: { name: "Jeremy Pyne", email: "jeremy.pyne@gmail.com"},&lt;br /&gt;  description: "Note this page in Google Reader.",&lt;br /&gt;  arguments: [ {role: 'object', nountype: noun_arb_text, label: 'note'} ],&lt;br /&gt;  execute: function( note ) {&lt;br /&gt;    var document = Application.activeWindow.activeTab.document;&lt;br /&gt;    var gc = "var b=document.body;var GR________bookmarklet_domain='https://www.google.com';if(b&amp;&amp;!document.xmlVersion){void(z=document.createElement('script'));void(z.src='https://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}";&lt;br /&gt;&lt;br /&gt;    void(z=document.createElement('script'));&lt;br /&gt;    z.appendChild(document.createTextNode(gc));&lt;br /&gt;    document.body.appendChild(z);&lt;br /&gt;&lt;br /&gt;    if(note.text)&lt;br /&gt;    {&lt;br /&gt;      var timer = Components.classes["@mozilla.org/timer;1"]&lt;br /&gt;                    .createInstance(Components.interfaces.nsITimer);&lt;br /&gt;      timer.initWithCallback(&lt;br /&gt;       function() {&lt;br /&gt;            document.getElementById("GR________link_bookmarklet_frame").contentDocument.getElementById("annotation").innerHTML= note.text;&lt;br /&gt;       },&lt;br /&gt;       1500,&lt;br /&gt;       Components.interfaces.nsITimer.TYPE_ONE_SHOT);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;})&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6172252093573510520?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6172252093573510520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6172252093573510520&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6172252093573510520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6172252093573510520'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/ubiquity-command-note-in-reader.html' title='Ubiquity Command: Note in Reader'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3630559391307570946</id><published>2008-10-10T18:39:00.002-04:00</published><updated>2008-10-10T18:53:18.361-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Gmail'/><title type='text'>Comfigure Gmai to stay online in Mail.</title><content type='html'>Here is another tip for Gmail users that want to work in Mail.app.  &lt;br /&gt;&lt;br /&gt;When you set up a Gmail account in Mail.app it will default to POP.  Now that Gmail supports IMAP you can use that instead.  The difference between the two is simply that POP downloads everything from the server and works locally where as IMAP access the server and works directly there.&lt;br /&gt;&lt;br /&gt;The two major benefits to using IMAP are that any changes you make to your mailbox locally. First of moving a message from one folder to anther is reflected on the server and secondly only a list of message is retrieved instead of the entire mailbox.  &lt;br /&gt;&lt;br /&gt;The second benefit isn't quite true though as Mail.app by default downloads the entire mailbox and all the attachments for offline viewing.  Now as I rarely use Mail.app and furthermore never use the offline mode, I disabled this feature and substantially improved the Gmail synchronization.  Not only the headers are cached and the message and attachments are retrieved when I open a message.&lt;br /&gt;&lt;br /&gt;To change this setting you will need to remove your Gmail account, to clear out the cache, and add it again, this time manually set it up and on the last screen uncheck "Take this account online".  This will prevent syncing.  Now go to the Advanced tab and change "Keep a copies of messages for offline viewing" to "Don't keep copies of any messages".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-3630559391307570946?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3630559391307570946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=3630559391307570946&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3630559391307570946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3630559391307570946'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/comfigure-gmai-to-stay-online-in-mail.html' title='Comfigure Gmai to stay online in Mail.'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2085374422095321107</id><published>2008-10-10T18:20:00.003-04:00</published><updated>2009-02-19T15:20:07.297-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='Gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='Fixes'/><title type='text'>Problem Syncing Gmail with Mail 3.0 in OS X 10.5</title><content type='html'>I general use Gmail via the web but I do have it configure in Mail.app for IMAP access as well for sending photos and files more easily.  I was trying to send some photos today and had a problem with mail hanging as it were.  It wasn't exactly crashing, but upon launching it would just sit there trying to sync and send files but never succeed.  This blocked access to the IMAP.  I verified the configuration but there were no problems.  &lt;br /&gt;&lt;br /&gt;As it turns out the problem was that Mail.app was trying to redo some task that had failed at an earlier time and these tasks were no invalid.  Probably send some photo's that no longer exist.  To clean up this problem just execute this command in a the terminal.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rm ~/Library/Mail/IMAP-*/.OfflineCache/*&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-2085374422095321107?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2085374422095321107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=2085374422095321107&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2085374422095321107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2085374422095321107'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/problem-syncing-gmail-with-mail-30-in.html' title='Problem Syncing Gmail with Mail 3.0 in OS X 10.5'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4693084024127154020</id><published>2008-10-07T15:02:00.004-04:00</published><updated>2008-10-27T15:42:38.536-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex DataGrid LookupColumn</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Update- 10/27/2008 3:27 pm&lt;/span&gt;&lt;br /&gt;Updated the LookupColumn control to search for items via a hash array.  The old code would loop through the source for each item witch would make the computation do R*N checks not a has array is created so that only R check's are needed.  This makes a big difference is there area are large number rows or lookup items.&lt;br /&gt;&lt;br /&gt;I highly recommend updating to this code to make renders run faster.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Here is another useful Flex component.  This is a DataGridColumn that can be used to do simple lookups from another source when the DataGrid is rendered.  You can use it to full data from a second source without having to first merge the data of write custom label functions.&lt;br /&gt;&lt;br /&gt;Here is a simple example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private const myGroups:ArrayCollection = new ArrayCollection(&lt;br /&gt;[{groupid: 1, name: "Admin"},&lt;br /&gt;{groupid: 2, name: "User"},&lt;br /&gt;{groupid: 3, name: "Guest"}]);&lt;br /&gt;&lt;br /&gt;&amp;lt;mx:DataGrid dataProvider="{}"&amp;gt;&lt;br /&gt;&amp;lt;mx:columns&amp;gt;&lt;br /&gt; &amp;lt;mx:DataGridColumn headerText="User Name" dataField="username"/&amp;gt;&lt;br /&gt; &amp;lt;LookupColumn headerText="Group" dataField="groupid" labelField="name" source="{myGroups}"/&amp;gt;&lt;br /&gt;&amp;lt;/mx:columns&amp;gt;&lt;br /&gt;&amp;lt;/mx:DataGrid&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;LookupColumn.as&lt;/span&gt;&lt;br /&gt;package hines&lt;br /&gt;{&lt;br /&gt; import flash.events.Event;&lt;br /&gt; &lt;br /&gt; import mx.collections.ArrayCollection;&lt;br /&gt; import mx.controls.dataGridClasses.DataGridColumn;&lt;br /&gt; &lt;br /&gt; [Exclude(name="dataTipField", kind="property")]&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * This class is a custom DataGridColumn taht will look up each item in another array and fill in a field from the found item in its place.&lt;br /&gt;  * &lt;br /&gt;  * Example:&lt;br /&gt;  * private const myGroups:ArrayCollection = new ArrayCollection(&lt;br /&gt;  *   [{groupid: 1, name: "Admin"},&lt;br /&gt;  *   {groupid: 2, name: "User"},&lt;br /&gt;  *   {groupid: 3, name: "Guest"}]);&lt;br /&gt;  * &lt;br /&gt;  * &amp;lt;hines:LookupColumn headerText="Group" dataField="groupid" labelField="groupname" source="{myGroups}"/&amp;gt;&lt;br /&gt;  * &lt;br /&gt;  * This will cause and rows with a groupid of 1 to render as "Admin" and so on.&lt;br /&gt;  * &lt;br /&gt;  * This will also work with a xml source and the lookupField can be used if the field names differ.&lt;br /&gt;  * &lt;br /&gt;  * &amp;lt;hines:LookupColumn headerText="Users Group" dataField="usersgroupid" lookupField="groupid" labelField="groupname" source="{myGroups}"/&amp;gt;&lt;br /&gt;  * &lt;br /&gt;  * @author jpyne&lt;br /&gt;  */&lt;br /&gt; public class LookupColumn extends DataGridColumn&lt;br /&gt; {&lt;br /&gt;  public function LookupColumn(columnName:String=null)&lt;br /&gt;  {&lt;br /&gt;   super(columnName);&lt;br /&gt;   &lt;br /&gt;   // Set up the internal lableFunction.&lt;br /&gt;   labelFunction = LookupColumn.doLookup;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  //----------------------------------&lt;br /&gt;  // labelField&lt;br /&gt;  //----------------------------------&lt;br /&gt;  &lt;br /&gt;  private var _labelField:String;&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Field to display in the destination record.  If this is not set it will default to "label".&lt;br /&gt;   */&lt;br /&gt;  [Inspectable(category="General", defaultValue="")]&lt;br /&gt;  [Bindable("labelFieldChanged")]&lt;br /&gt;  public function get labelField():String&lt;br /&gt;  {&lt;br /&gt;   return _labelField ? _labelField : "label";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  @private&lt;br /&gt;   */&lt;br /&gt;  public function set labelField(value:String):void&lt;br /&gt;  {&lt;br /&gt;   _labelField = value;&lt;br /&gt;   &lt;br /&gt;   dispatchEvent(new Event("labelFieldChanged"));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  //----------------------------------&lt;br /&gt;  // lookupField&lt;br /&gt;  //----------------------------------&lt;br /&gt;  &lt;br /&gt;  private var _lookupField:String;&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Field to search for in the source ArrayCollection.  If unset then datafield will be used.&lt;br /&gt;   */&lt;br /&gt;  [Inspectable(category="General", defaultValue="")]&lt;br /&gt;  [Bindable("lookupFieldChanged")]&lt;br /&gt;  public function get lookupField():String&lt;br /&gt;  {&lt;br /&gt;   return _lookupField ? _lookupField : dataField;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  @private&lt;br /&gt;   */&lt;br /&gt;  public function set lookupField(value:String):void&lt;br /&gt;  {&lt;br /&gt;   _lookupField = value;&lt;br /&gt;   &lt;br /&gt;   if(source)&lt;br /&gt;    prepSource();&lt;br /&gt;   &lt;br /&gt;   dispatchEvent(new Event("lookupFieldChanged"));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  //----------------------------------&lt;br /&gt;  // source&lt;br /&gt;  //----------------------------------&lt;br /&gt;  &lt;br /&gt;  private var _source:ArrayCollection;&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Field to display in the destination record.  If this is not set it will default to "label".&lt;br /&gt;   */&lt;br /&gt;  [Inspectable(category="General", defaultValue="")]&lt;br /&gt;  [Bindable("sourceChanged")]&lt;br /&gt;  public function get source():ArrayCollection&lt;br /&gt;  {&lt;br /&gt;   return _source;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   *  @private&lt;br /&gt;   */&lt;br /&gt;  public function set source(value:ArrayCollection):void&lt;br /&gt;  {&lt;br /&gt;   _source = value;&lt;br /&gt;   &lt;br /&gt;   if(lookupField)&lt;br /&gt;    prepSource();&lt;br /&gt;   &lt;br /&gt;   dispatchEvent(new Event("sourceChanged"));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  //----------------------------------&lt;br /&gt;  // hashSource&lt;br /&gt;  //----------------------------------&lt;br /&gt;  &lt;br /&gt;  private var _hashSource:Object;&lt;br /&gt;  &lt;br /&gt;  public function get hashSource():Object&lt;br /&gt;  {&lt;br /&gt;   return _hashSource;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  private function prepSource():void&lt;br /&gt;  {&lt;br /&gt;   _hashSource = new Object();&lt;br /&gt;   &lt;br /&gt;   for each (var item:Object in source)&lt;br /&gt;   {&lt;br /&gt;    _hashSource[item[lookupField]] = item;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Override the dataTipFiled and change it to the dataField as that will be hidden.&lt;br /&gt;   */&lt;br /&gt;  override public function get dataTipField():String&lt;br /&gt;  {&lt;br /&gt;   return dataField;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Look for a item in a source and display its labelField.  This is an internal method and will be called for each item in teh DataGrid.&lt;br /&gt;   * &lt;br /&gt;   * @param row&lt;br /&gt;   * @param control&lt;br /&gt;   * @return &lt;br /&gt;   */&lt;br /&gt;  static private function doLookup(row:Object, control:LookupColumn):String&lt;br /&gt;  {&lt;br /&gt;   if(control.hashSource &amp;&amp; control.hashSource[row[control.dataField]])&lt;br /&gt;    return control.hashSource[row[control.dataField]][control.labelField];&lt;br /&gt;   &lt;br /&gt;   // If no match was found, just return the original value.&lt;br /&gt;   return row[control.dataField];&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4693084024127154020?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4693084024127154020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4693084024127154020&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4693084024127154020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4693084024127154020'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/flex-datagrid-lookupcolumn.html' title='Flex DataGrid LookupColumn'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4589234387000378845</id><published>2008-10-06T14:14:00.004-04:00</published><updated>2008-10-06T19:40:59.629-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Auto Mount/Unmout Disk Images</title><content type='html'>Here are two scripts to automatically mount and unmout image files in Linux.&lt;br /&gt;&lt;br /&gt;Console Script:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;/usr/local/bin/isomnt&lt;/span&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;if [ -e "$*" ]; then&lt;br /&gt;if [ -d /media/"$*" ]; then&lt;br /&gt;  echo "Unmounting '$*'..."&lt;br /&gt;  sudo umount -d /media/"$*"&lt;br /&gt;  sudo rmdir /media/"$*"&lt;br /&gt;else&lt;br /&gt;  echo "Mounting '$*'..."&lt;br /&gt;  sudo mkdir /media/"$*"&lt;br /&gt;  sudo mount -o loop "$*" /media/"$*"&lt;br /&gt;fi&lt;br /&gt;else&lt;br /&gt;  echo "No image found to mount."&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Nautilus Script:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;~/.gnome2/nautilus-scripts/(Un)Mount ISO&lt;/span&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;if [ -d /media/"$*" ]; then&lt;br /&gt;  gksudo ls /media&lt;br /&gt;  sudo umount -d /media/"$*"&lt;br /&gt;  sudo rmdir /media/"$*"&lt;br /&gt;else&lt;br /&gt;  gksudo mkdir /media/"$*"&lt;br /&gt;  wd=${NAUTILUS_SCRIPT_CURRENT_URI#file://}&lt;br /&gt;  sudo mount -o loop "$wd/$*" /media/"$*"&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4589234387000378845?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4589234387000378845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4589234387000378845&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4589234387000378845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4589234387000378845'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/auto-mountunmout-disk-images.html' title='Auto Mount/Unmout Disk Images'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-5947879596494003668</id><published>2008-10-06T14:04:00.002-04:00</published><updated>2008-10-06T14:09:31.265-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><title type='text'>MediaWiki Blacklist Extension</title><content type='html'>This is an older extension I wrote for MediaWiki to allow the blacklisting of various pages.&lt;br /&gt;&lt;br /&gt;To install the extention just ave the code below to blacklist.php in MediaWiki's extensions folder and add the following line in the LocalSettings.php file:&lt;br /&gt;&lt;pre&gt;include_once("extensions/blacklist.php");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Example: To block some special pages for normal users, but not sysops, do this.&lt;br /&gt;&lt;pre&gt;$wgWhitelist['sysop']['read']  = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or with a RegEx:&lt;br /&gt;&lt;pre&gt;$wgBlacklistOps["useRegex"] = true;&lt;br /&gt;$wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight:bold;"&gt;blacklist.php&lt;/span&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;/*&lt;br /&gt;    Blacklist Mediawiki Extension&lt;br /&gt;    By Jeremy Pyne  jeremy.pyne@gmail.com&lt;br /&gt; &lt;br /&gt;    This extension adds support for a $wgBlacklist array, layed out like $wgGroupPermissions, to support overrides.&lt;br /&gt;        For example I can set $wgBlacklist['*']['read']  to diable specific special pages or&lt;br /&gt;        make some pages of the site only visible for special groups.&lt;br /&gt;    This blacklisting is done from lowest to highest powered groups and is implisit.  IE if you deny Main Page to User, it also denies it for all parent's of user.&lt;br /&gt;        To override a blacklist at a higher level ou have to add an entry to $$wgWhitelist['sysop']['read'] to re-enable the pages if you are a sysop.&lt;br /&gt; &lt;br /&gt;    Options: &lt;br /&gt;        $wgBlacklistOps["useRegex"] = true;&lt;br /&gt;                This setting dictates whether to tread the page lists as regular expressions or not.  Though turning regular expressions off is much faster, you can not &lt;br /&gt;                mark page groups, partial page titles, or variations of title formating.&lt;br /&gt; &lt;br /&gt;    Example: To block some special pages for normal users, but not sysops do this.&lt;br /&gt;        $wgWhitelist['sysop']['read']  = $wgBlacklist['*']['read'] = array("Special:Export", "Special:Listusers", "Special:Ipblocklist", "Special:Log", "Special:Allmessages");&lt;br /&gt;    Or wth a RegEx&lt;br /&gt;        $wgBlacklistOps["useRegex"] = true;&lt;br /&gt;        $wgWhitelist['sysop']['read'] = $wgBlacklist['*']['read'] = array("^Special:(Export|Listusers|Ipblocklist|Log|Allmessages)$");&lt;br /&gt; &lt;br /&gt;     Note: This is not flawless method as page inclusions and such can get around this.&lt;br /&gt;*/&lt;br /&gt; &lt;br /&gt;if (!defined('MEDIAWIKI')) die();&lt;br /&gt; &lt;br /&gt;$wgExtensionCredits['other'][] = array(&lt;br /&gt;    'name' =&gt; 'blacklist',&lt;br /&gt;    'description' =&gt; 'adds $wgBlacklist array to provide blacklist overrides',&lt;br /&gt;    'url' =&gt; 'http://www.mediawiki.org/wiki/Extension:Blacklist',&lt;br /&gt;    'author' =&gt; 'Jeremy Pyne',&lt;br /&gt;    'version' =&gt; '1.0'&lt;br /&gt;);&lt;br /&gt; &lt;br /&gt;$wgHooks['userCan'][] = 'checkBlacklist';&lt;br /&gt; &lt;br /&gt;/**&lt;br /&gt; * Is this page blacklisted&lt;br /&gt; * @param &amp;$title the concerned page&lt;br /&gt; * @param &amp;$wgUser the current mediawiki user&lt;br /&gt; * @param $action the action performed&lt;br /&gt; * @param &amp;$result (out) true or false, or null if we don't care about the parameters&lt;br /&gt; */&lt;br /&gt;function checkBlacklist(&amp;$title, &amp;$wgUser, $action, &amp;$result) {&lt;br /&gt;        global $wgBlacklist;&lt;br /&gt;        global $wgWhitelist;&lt;br /&gt;        global $wgBlacklistOps;&lt;br /&gt;        $hideMe = false;&lt;br /&gt; &lt;br /&gt;        $groupPower = array(&lt;br /&gt;                0 =&gt; "*",&lt;br /&gt;                1 =&gt; "user",&lt;br /&gt;                2 =&gt; "autoconfirmed",&lt;br /&gt;                3 =&gt; "emailconfirmed",&lt;br /&gt;                4 =&gt; "bot",&lt;br /&gt;                5 =&gt; "sysop",&lt;br /&gt;                6 =&gt; "bureaucrat");&lt;br /&gt;        $myGroups = array_intersect($groupPower, $wgUser-&gt;getEffectiveGroups());&lt;br /&gt; &lt;br /&gt;        foreach($myGroups as $myGroup) {&lt;br /&gt;                if(array_key_exists($myGroup, $wgBlacklist) &amp;&amp; array_key_exists($action, $wgBlacklist[$myGroup]) &amp;&amp;  is_array($wgBlacklist[$myGroup][$action]))&lt;br /&gt;                {&lt;br /&gt;                        if($wgBlacklistOps["useRegex"]) {&lt;br /&gt;                                foreach($wgBlacklist[$myGroup][$action] as $myBlacklist)&lt;br /&gt;                                        if(preg_match("/$myBlacklist/", $title-&gt;getPrefixedText()))&lt;br /&gt;                                        {&lt;br /&gt;                                                $hideMe = true;&lt;br /&gt;                                                break;&lt;br /&gt;                                        }&lt;br /&gt;                        } else {&lt;br /&gt;                                $myBlacklist = array_flip($wgBlacklist[$myGroup][$action]);&lt;br /&gt;                                if(array_key_exists($title-&gt;getPrefixedText(), $myBlacklist))&lt;br /&gt;                                        $hideMe = true;&lt;br /&gt;                        }&lt;br /&gt;                }&lt;br /&gt; &lt;br /&gt;                if(array_key_exists($myGroup, $wgWhitelist) &amp;&amp; array_key_exists($action, $wgWhitelist[$myGroup]) &amp;&amp;  is_array($wgWhitelist[$myGroup][$action]))&lt;br /&gt;                {&lt;br /&gt;                        if($wgBlacklistOps["useRegex"]) {&lt;br /&gt;                                foreach($wgWhitelist[$myGroup][$action] as $myWhitelist)&lt;br /&gt;                                        if(preg_match("/$myWhitelist/", $title-&gt;getPrefixedText()))&lt;br /&gt;                                        {&lt;br /&gt;                                                $hideMe = false;&lt;br /&gt;                                                break;&lt;br /&gt;                                        }&lt;br /&gt;                        } else {&lt;br /&gt;                                $myWhitelist = array_flip($wgWhitelist[$myGroup][$action]);&lt;br /&gt;                                if(array_key_exists($title-&gt;getPrefixedText(), $myWhitelist))&lt;br /&gt;                                        $hideMe = false;&lt;br /&gt;                        }&lt;br /&gt;                }&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        if($hideMe)&lt;br /&gt;                $result = false;&lt;br /&gt; &lt;br /&gt;        return !$hideMe;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-5947879596494003668?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/5947879596494003668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=5947879596494003668&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5947879596494003668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/5947879596494003668'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/mediawiki-blacklist-extension.html' title='MediaWiki Blacklist Extension'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6101893357268764353</id><published>2008-10-06T13:21:00.006-04:00</published><updated>2010-11-23T14:57:02.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='Smarty'/><title type='text'>Switch Statment for Smarty</title><content type='html'>There is a complete and working switch plugin. It works exactly as a php switch and you can even use variables and modifiers for the conditions. Feel free to use it and let me know if you have and problems.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;11/23/2010&lt;/span&gt;&lt;br /&gt;I moved the code to github for matinance. &lt;a href="https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-2.0"&gt;https://github.com/pynej/Smarty-Switch-Statement/archives/Smarty-2.0&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;03/08/2008 - Updated to version 2&lt;/span&gt;&lt;br /&gt;This update changes the break attribute to work how you would expect. That is, setting the break attribute will cause smarty to automatically render a {break} tag before it starts the NEXT case or default section. I strongly recommend updating to this version of the switch plugin, as the older version was very counter intuitive in this aspect.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;02/09/2010 - Posted Smarty 3 Version&lt;/span&gt;&lt;br /&gt;A separate version of this plug-in is available for Smarty 3 &lt;a href="http://pynej.blogspot.com/2010/02/switch-statment-for-smarty-3.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(Also, this does work with nested switches, witch is why you see it accessing the _switchData variable. Be sure to not alter this variable in smarty code, as it will make the switches behave incorrectly.)&lt;br /&gt;&lt;br /&gt;Both of these blocks will produce identical switch logic.&lt;br /&gt;&lt;pre&gt;{case 1 break}&lt;br /&gt;Code 1&lt;br /&gt;{case 2}&lt;br /&gt;Code 2&lt;br /&gt;{default break}&lt;br /&gt;Code 3&lt;br /&gt;&lt;br /&gt;{case 1}&lt;br /&gt;Code 1&lt;br /&gt;{break}&lt;br /&gt;{case 2}&lt;br /&gt;Code 2&lt;br /&gt;{default}&lt;br /&gt;Code 3&lt;br /&gt;{break}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;03/08/2008 - Updated to version 2.1&lt;/span&gt;&lt;br /&gt;Added {/case} tag, this is identical to {break}.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-6101893357268764353?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6101893357268764353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=6101893357268764353&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6101893357268764353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6101893357268764353'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/switch-statment-for-smarty.html' title='Switch Statment for Smarty'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1622459722939420229</id><published>2008-10-06T11:50:00.000-04:00</published><updated>2008-10-06T12:00:06.736-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MediaWiki'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Extensions'/><title type='text'>MediaWiki Tidy URLs</title><content type='html'>This article describes how to configure nice URLs on MediaWiki.  The &lt;a href="http://www.mediawiki.org/wiki/Nice_url"&gt;wiki page&lt;/a&gt; on the topic is rather massive and hard to understand so here are the instructions for Apache2.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;Before:&lt;/span&gt;&lt;br /&gt;http://my.domain.com/wiki/index.php?title=Main_Page&amp;amp;action=view&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;After:&lt;/span&gt;&lt;br /&gt;http://my.domain.com/wiki/Main_Page/view&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add the following likes at the end of LocalSettings.php. Update $wgScriptPath to reflect you install.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;LocalSettings.php&lt;/span&gt;&lt;br /&gt;$wgScriptPath  = "/wiki";&lt;br /&gt;&lt;br /&gt;$wgArticlePath = "$wgScriptPath/$1";&lt;br /&gt;&lt;br /&gt;$actions = array('view', 'edit', 'watch', 'unwatch', 'delete','revert', 'rollback', 'protect',&lt;br /&gt;              'unprotect','info','markpatrolled','validate','render','deletetrackback','print',&lt;br /&gt;              'dublincore','creativecommons','credits','submit','viewsource','history','purge');&lt;br /&gt;&lt;br /&gt;foreach ($actions as $a)&lt;br /&gt;$wgActionPaths[$a] = "$wgScriptPath/$1/$a";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Enable the loading of the mod_rewrite.c module.&lt;br /&gt;&lt;pre&gt;LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add the following to your httpd.conf or virtual site. Again, replace /wiki as needed.&lt;br /&gt;&lt;pre&gt;RewriteCond %{REQUEST_URI} /(view|edit|watch|unwatch|delete|revert|rollback|protect|unprotect|info|markpatrolled|validate|render|deletetrackback|print$&lt;br /&gt;RewriteCond %{REQUEST_FILENAME} !-f&lt;br /&gt;RewriteCond %{REQUEST_FILENAME} !-d&lt;br /&gt;RewriteRule ^/wiki/(.*)/([a-z]*)$ /wiki/index.php?title=$1&amp;amp;action=$2 [L,QSA]&lt;br /&gt;&lt;br /&gt;RewriteCond %{REQUEST_URI} !/wiki/images&lt;br /&gt;RewriteCond %{REQUEST_URI} !/wiki/skins&lt;br /&gt;RewriteCond %{REQUEST_FILENAME} !-f&lt;br /&gt;RewriteCond %{REQUEST_FILENAME} !-d&lt;br /&gt;RewriteRule ^/wiki/(.*)$ /wiki/index.php?title=$1 [PT,L,QSA]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-1622459722939420229?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1622459722939420229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=1622459722939420229&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1622459722939420229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1622459722939420229'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/mediawiki-tidy-urls.html' title='MediaWiki Tidy URLs'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4593741562900501231</id><published>2008-10-06T10:09:00.003-04:00</published><updated>2008-10-10T09:55:27.775-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex DateUtil Class</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Upadte- 10/8/2008 1:26 PM&lt;/span&gt;&lt;br /&gt;Added DateUtil.create() and DateUtil.clone() static methods.  These can be used to work with a existing date.  The Clone will cause updates to the original source where as the clone will not.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;DateUtil.clone(StartDate.selectedDate).date;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Upadte- 10/10/2008 9:54 AM&lt;/span&gt;&lt;br /&gt;Fixed a problem with the getters corrupting the date.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;When working with dates and times in flex I found myself wanting to make simple changes like adding a day or trimming off the time witch turned out to be quite a few lines of code.  Here is a example of setting a Date Field to yesterday and trimming off the time.&lt;br /&gt;&lt;pre&gt;[Bindable]&lt;br /&gt;private var prevDate:Date;&lt;br /&gt;&lt;br /&gt;prevDate.hours = prevDate.minutes = prevDate.seconds = prevDate.milliseconds = 0;&lt;br /&gt;prevDate.date--;&lt;br /&gt;&lt;br /&gt;// Update the DateField.&lt;br /&gt;dtDate.selectedDate = prevDate;&lt;br /&gt;dtDate.dispatchEvent(new CalendarLayoutChangeEvent(CalendarLayoutChangeEvent.CHANGE));&lt;br /&gt;&lt;br /&gt;&amp;lt;mx:datefield id="dtDate" change="" selecteddate="{prevDate}"&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see this is a rather large chunk of code to just change the date so I decided to create a DateUtil class.  Here is the same operation using the DateUtil class.&lt;br /&gt;&lt;pre&gt;&amp;lt;hines:DateField id="dtDate" change="" selectedDate="{DateUtil.now.subtract(DateUtil.DATE, 1).date}"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here are some more examples of the DateUtil class.&lt;br /&gt;&lt;pre&gt;var time:Date = DateUtil.now.time;&lt;br /&gt;var monday:Date = DateUtil.now.assign(DateUtil.DAY, 1).date;&lt;br /&gt;var then:Date = DateUtil.now.add(DateUtil.HOURS, 2).datetime;&lt;br /&gt;var day:Number = DateUtil.now.fetch(DateUtil.Day);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the custom class.  Feel free to use it just credit me.  If you have any problems let me know.&lt;br /&gt;&lt;pre&gt;&lt;span style="font-weight: bold;"&gt;DateUtil.as&lt;/span&gt;&lt;br /&gt;package&lt;br /&gt;{&lt;br /&gt; /**&lt;br /&gt;  * DateUtil class for simple date manipulation.&lt;br /&gt;  *&lt;br /&gt;  * You can do simple time alterations with this calss.  Here are some samples.&lt;br /&gt;  *&lt;br /&gt;  * Subtract a day:&lt;br /&gt;  *         DateUtil.now.subtract(DateUtil.DATE, 1).date;&lt;br /&gt;  *&lt;br /&gt;  * Add a month:&lt;br /&gt;  *         DateUtil.now.add(DateUtil.MONTH, 1).date;&lt;br /&gt;  *&lt;br /&gt;  * Go to Monday:&lt;br /&gt;  *         DateUtil.now.assign(DateUtil.DAY, 1).date&lt;br /&gt;  *&lt;br /&gt;  * @author Jeremy Pyne jeremy.pyne@gmail.com&lt;br /&gt;  */&lt;br /&gt; public class DateUtil&lt;br /&gt; {&lt;br /&gt;     /**&lt;br /&gt;      * Date storage object.&lt;br /&gt;      */&lt;br /&gt;     private var _date:Date;&lt;br /&gt; &lt;br /&gt;     public static const MILLISECONDS:String = "milliseconds";&lt;br /&gt;     public static const SECONDS:String = "seconds";&lt;br /&gt;     public static const MINUTES:String = "minutes";&lt;br /&gt;     public static const HOURS:String = "hours";&lt;br /&gt;     public static const DAY:String = "day";&lt;br /&gt;     public static const DATE:String = "date";&lt;br /&gt;     public static const MONTH:String = "month";&lt;br /&gt;     public static const YEAR:String = "fullYear";&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Create a new DateUtil class.&lt;br /&gt;      *&lt;br /&gt;      * @param date&lt;br /&gt;      */&lt;br /&gt;     public function DateUtil(date:Date)&lt;br /&gt;     {&lt;br /&gt;         _date = date;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Add some value to a variable.&lt;br /&gt;      *&lt;br /&gt;      * @param variable&lt;br /&gt;      * @param value&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function add(variable:String, value:Number):DateUtil&lt;br /&gt;     {&lt;br /&gt;         if(variable == DateUtil.DAY)&lt;br /&gt;             variable = DateUtil.DATE;&lt;br /&gt;     &lt;br /&gt;         _date[variable] += value;&lt;br /&gt;         return this;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Subtract some value to a variable.&lt;br /&gt;      *&lt;br /&gt;      * @param variable&lt;br /&gt;      * @param value&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function subtract(variable:String, value:Number):DateUtil&lt;br /&gt;     {&lt;br /&gt;         if(variable == DateUtil.DAY)&lt;br /&gt;             variable = DateUtil.DATE;&lt;br /&gt;     &lt;br /&gt;         _date[variable] -= value;&lt;br /&gt;         return this;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Set some value to a variable.&lt;br /&gt;      *&lt;br /&gt;      * @param variable&lt;br /&gt;      * @param value&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function assign(variable:String, value:Number):DateUtil&lt;br /&gt;     {&lt;br /&gt;         if(variable == DateUtil.DAY)&lt;br /&gt;         {&lt;br /&gt;             variable = DateUtil.DATE;&lt;br /&gt;             value = _date.date + value - _date.day;&lt;br /&gt;         }&lt;br /&gt;     &lt;br /&gt;         _date[variable] = value;&lt;br /&gt;         return this;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Get a variable.&lt;br /&gt;      *&lt;br /&gt;      * @param variable&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function fetch(variable:String):Number&lt;br /&gt;     {&lt;br /&gt;         return _date[variable];&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Get only the current date.&lt;br /&gt;      *&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function get date():Date&lt;br /&gt;     {&lt;br /&gt;         var date:Date = new Date(_date.time);&lt;br /&gt;         date.hours = date.minutes = date.seconds = date.milliseconds = 0;&lt;br /&gt;         return date;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Get only the current time.&lt;br /&gt;      *&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function get time():Date&lt;br /&gt;     {&lt;br /&gt;         var date:Date = new Date(_date.time);&lt;br /&gt;         date.fullYear = 1969;&lt;br /&gt;         date.month = date.date = 0;&lt;br /&gt;         return date;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Get the full date and time.&lt;br /&gt;      *&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public function get datetime():Date&lt;br /&gt;     {&lt;br /&gt;         var date:Date = new Date(_date.time);&lt;br /&gt;         return date;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;     /**&lt;br /&gt;      * Create a new DateUtil for the current data and time.&lt;br /&gt;      *&lt;br /&gt;      * @return&lt;br /&gt;      */&lt;br /&gt;     public static function get now():DateUtil&lt;br /&gt;     {&lt;br /&gt;         return new DateUtil(new Date);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     /**&lt;br /&gt;      * Create a new DateUtil for the passed in date.  Updates will not effect the original date.&lt;br /&gt;      * &lt;br /&gt;      * @return &lt;br /&gt;      */&lt;br /&gt;     public static function clone(date:Date):DateUtil&lt;br /&gt;     {&lt;br /&gt;          return new DateUtil(new Date(date.time));&lt;br /&gt;     }&lt;br /&gt;     &lt;br /&gt;     /**&lt;br /&gt;      * Create a new DateUtil for the passed in date. Updates will alter the original date.&lt;br /&gt;      * &lt;br /&gt;      * @return &lt;br /&gt;      */&lt;br /&gt;     public static function create(date:Date):DateUtil&lt;br /&gt;     {&lt;br /&gt;          return new DateUtil(date);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-4593741562900501231?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4593741562900501231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=4593741562900501231&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4593741562900501231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4593741562900501231'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/flex-dateutil-class.html' title='Flex DateUtil Class'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-8977213561561728608</id><published>2008-10-06T09:57:00.000-04:00</published><updated>2008-10-06T10:09:09.322-04:00</updated><title type='text'>Hello World</title><content type='html'>Hello hello.  I am just getting started but here is a short list of the kind of thinks to expect here.  I will try to keep things labeled properly for sanity.&lt;ul&gt;&lt;li&gt;Computers, Programming, Scripting&lt;/li&gt;&lt;li&gt;Video, Photography&lt;/li&gt;&lt;li&gt;Video games, Anime&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8526508655697471701-8977213561561728608?l=pynej.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/8977213561561728608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8526508655697471701&amp;postID=8977213561561728608&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8977213561561728608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/8977213561561728608'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2008/10/hello-world.html' title='Hello World'/><author><name>Jeremy Pyne</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
