Showing posts with label Technology. Show all posts
Showing posts with label Technology. Show all posts

Monday, October 14, 2013

HydraPaper update

I've been using HydraPaper at home and work for several months now and noticed a few minor bugs and missing features.  I've made a small update that includes the following:
  • Using the tray icon Exit menu now actually exits.
  • Running out of one type of wallpaper doesn't reset the list for the other type. This bug made it so it didn't always cycle through every image before starting over again.
  • HydraPaper now stops cycling the wallpaper when a remote desktop session begins.
  • Orientation EXIF data in JPGs is now honored.  All those sideways family vacation pictures are now automatically rotated when displayed (assuming your camera stores orientation data).
I used a library called EXIFextractor from CodePage.com to get info on the image's orientation and used standard .NET drawing statements to rotate the image.

Download

The updated version: HydraPaper v1.1 from Dropbox

Thursday, February 21, 2013

HydraPaper

Some friends of mine have begun writing about their programming exploits and I've found their experiences very interesting.  Since I haven't written in quite awhile I thought posting a couple of little utilities that I've created would be fun.


HydraPaper

My most recent project is called HydraPaper.  It is a wallpaper rotator for those with multi-headed systems.  Of course it will work for a single monitor just as well and has features that are useful even if you don't have two or more monitors.

Features

  • Resizes large or small wallpapers to fit based on your preference
  • Creates a single large wallpaper from several individual wallpapers that will span all your monitors correctly based on your screen resolution and positions as setup in the Windows Control Panel
  • Supports and resizes muli-monitor wallpapers. These are sized to properly span the single image across all your monitors.
  • Automatically detects changes in your screen configuration and creates a new wallpaper immediately.
  • Suspends when you lock your computer and resumes when you unlock it.
  • Avoids repeating wallpapers until they have all been shown
  • Runs quietly in the System Tray

 

Download

Download ZIP from DropBox.  There is no installer. Check the readme.txt file. This project was build in C# using Visual Studio 2010 and so requires the .Net 4.0 Framework to run.

Experience

I decided to work on this project as a respite from the tiring coding projects going on at work.  I had this project in the back of my mind for awhile.  I was growing tired of manually constructing wide screen wallpapers at the exact resolution I needed to get the Tile setting to get it to span multiple monitors.  It got more difficult when I began using one monitor in portrait orientation rather than the standard landscape orientation.  The manual process for creating a working multi-monitor wallpaper became the basis for the method I use in HydraPaper.

The first step in the project was to find out how to programmatically set the wallpaper.  This has already been done and I used the code library found here. It works well and has a simple interface.

I began by attempting to replicate the Windows Screen Resolution tool where each monitor is represented by a numbered graphic.  I got this to work and you could set per screen settings. But after some testing I found all this was overkill.  It was an interesting exercise but it wasn't necessary to have individual screen settings and so the fancy UI was pretty but unnecessary.  So out it went and I simplified down to the current Folder and Resize method UI.

After further testing I decided I needed to support wallpapers that were already formatted for multiple monitors since some of my favorite wallpapers in my collection were in this format.  So I cloned the single wallpaper settings UI to support these images as well.

To keep it simple I don't try to detect whether an image has the right proportions to be treated as a multi-monitor wallpaper. Instead I you have to divide up the images into separate folders.

The default folder selection dialog in windows uses a folder tree. I have always hated this dialog. You can't paste a path into the address or selection bar and have to drill in every time.  I wanted the regular file selection dialog but for folders.  This was solved by using the Ookii dialogs library. Awesome. UI is done.

The next step was working out how to resize images.  I already had in mind to support a variety of resize options. These include touch outside (which resizes and crops to fit), touch inside (which resizes until the whole image fits leaving blank areas on the sides), center (does not resize, just centers), and stretch (which resizes to fit and does not preserve the image's aspect ratio).  In the end I only use touch outside option but the work is done so I left the options.

Getting a list of all the screens and their resolution is very easy in C#. The System.Windows.Forms.Screen namespace provides access and data about each screen.  I have access to the screen's width and height and the offset from the primary screen.  The offset can have negative numbers if the screen is to the left or above the primary screen.


Since my image can't have negative X, Y coordinates (well, it turns that a C# graphics canvas can but it was too late when I found that out) I need to determine how much to offset my grid so I have room to draw images for the negative offset screens at positive image coordinates.  This fixes it so my negative offset screens are translated to positive image coordinates but it moves the primary screen to no longer be drawn at (0,0).  I'll have to take steps to correct the image later since the (0,0) coordinate anchors to the primary monitor's (0,0) coordinate rather than the top left monitor.

Now I resize, clip and draw each image to its offset position and I end with a very large wallpaper that looks like just what you'd want to see on your screens.

But due to the primary anchoring behavior I have to shift the image back to get (0,0) to line up with (0,0) again.  To do this I create another large graphic.  I take the image and clone from my offset primary position and draw it at (0,0) on the new graphic.  Now my new graphic is positioned properly but I'm missing everything to the left and above the primary.  So I take everything above the primary offset and draw it at the bottom of the new graphic.  Then I take everything to the left of the primary offset and draw it at the right of the new graphic.  The new chopped up image now anchors properly and will wrap to the negative offset screens.

The new graphic becomes my final wallpaper and is saved as a JPG and is set as the desktop's wallpaper. You can find the rendered wallpaper under your %LocalAppData%/HydraPaper folder. If your primary monitor is your top/left-most monitor you won't see any funny chopping. To get an idea of how the image wraps try adjusting the positions of your monitors in the Control Panel.

I avoid repeating wallpapers by flagging each file used until I run out of files and then I can start to reuse the files again.  Before each wallpaper update I rescan the folders to check for new or missing files and update the internal list.

And that is it. It took me awhile to wrap my head around the math for properly scaling, clipping and positioning each part of the image but I got there in the end.  My math really has gotten rusty since my school days.

I built the project on .Net 4.0 but there isn't anything special about .Net 4.0 used in the project except for some of the Linq-to-object methods called on some collections.

Thursday, May 27, 2010

Setting up a custom Team Build project

Our company has Team Server 2008 and Team Build agents all set up and ready to go, except that no one really knows what to do with it. We don't have unit tests and we don't do code analysis and our deployment process is quite manual. And we haven't really established what the Microsoft compatible method is for organizing our version control repository.

Microsoft products are very "in the box". By that I mean, if you are going to do what Microsoft envisioned you would do then everything is very easy (if you can figure out what it is they envisioned). As soon as you step outside the box you're in for a world of pain.

As with many Microsoft tools it seems that in order to understand the tool have to first understand the tool. So, with this post, I hope to help some other newbies get started.

So, to help ease the pain (but while realizing this is the blind leading the blind) here are some get started tips.

Our scenario was to simply pull the latest version a website from TFS and deploy it to a test web server. We wanted this to happen automatically when a check-in occurred. As simple as this sounds it is not "in the box".

The box is this:
  1. Optionally get the latest code from TFS (with options for incremental get, cleaning, overwritting, etc).
  2. Optionally clean the build
  3. Optionally modify the default Drop location format
  4. Build
  5. Optionally Test
  6. Optional Code Analysis
  7. Semi-optionally drop the build to a network share
I am assuming you've already set up a TFS server and a TFS Build Agent on the same or a different server. I didn't have to do that and don't know anything about it.

All the configuration is handled inside Visual Studio which remotely configures the Build Server. If you are using TFS 2008 then you must use Visual Studio 2008 (with the Team Explorer component installed). If you are using Visual Studio 2010 with TFS 2008 you will not be able to "Manage Build Agents" which is a required piece.

Step 1: Set up the Build Agent (this all happens in Visual Studio)
  • Using the Build menu select Manage Build Agents
  • Give any display name and description
  • Enter the computer name where you installed the TFS Build Agent service
  • Enter the port (it'll probably be the default)
  • The working directory is the location out on the computer were the TFS Build Agent is installed where all the temporary files will be placed (source code, logs, build output, etc). This includes a TFS MSBuild variable thingy called $(BuildDefinitionPath). Leave this in your path so the files in this build stay separated from other builds. This variable is assigned the name of the Build Definition that you create later.
  • They give you a bunch of warnings about having sufficient disk space (since in their scenarios builds take hours and a failed build is the worse thing possible).
  • Set the Agent to Enabled.
One Build Agent can only serve a single TFS server but can build lots of different projects from that TFS server.

Step 2: Build Definition

The build definition links together the source control path (so it knows what files to get), the build agent and the network share where it drops the output files. It also helps you create a basic TFSBuild.proj project file (it's just XML) where you customize what happens in the automated build.

In our environment we pretty much only use the Source Control portion of TFS (we don't use Work Items, Reports or Documentation). So I have mental blinders when I connect to the TFS server in Visual Studio (Team Explorer) and always immediately open the Source Control window without ever looking at the other items. One of those items is Builds. This is where you define your Build Definitions.

Right click on Builds and choose New Build Definition from the context menu. This gives you the Build Definition Dialog:
  • Give the Build Definition a name
  • Defined a workspace. My initial workspace was full of unrelated items. Delete everything you don't need as part of the build and add only those you need. Or, you can use the Copy Existing Workspace to pull in one that you are already using in Visual Studio
  • Next create a project file. Project files are stored and executed from within TFS. So create a place in TFS to store the project files. We store ours in a separate location from our projects (a separate TeamBuild folder with a subfolder for each Build Definition). Use the Create... button to get a default TFSBuild.proj file created for you. Here you choose which solution to build. Choose your tests and code analysis. Our project didn't use any of these so you're one your own. Once you've finished the Create... wizard new TFSBuild.proj and TFSBuild.rsp files will be created in the location you specified. This TFSBuild.proj file is where you customize your build.
  • Next choose your retention policy. The output from each is kept. During your initial setup you might want to keep everything so you can review the logs. You can go back and revisit what you want to keep after everything is finished. In the end you'll probably only want to keep Failed or Partially Succeeded builds so you can review the logs.
  • Next choose the Build Agent you setup in the last step. You are also required to choose a network share where the build will be "dropped" (a place where the result will be copied to). For some reason it MUST be a network share. Also, the user you configured the Team Build Service to execute as must have Full Permission access to that share. Also, the build agent will automatically create a drop folder on the share you specify (so you can use one share for lots of different Build Definitions).
  • Choose your desired trigger. During your initial setup you probably want to select "Check-ins do not trigger a new build". The amounts to "Manual Build". This way you can tweak your automated build and manually execute it rather than being tied to some other build trigger. Later you can return and select the most appropriate option.
Now you've got a build definition. And if you just want to Build, Test, etc. you are probably done. In our case we don't want to build at all. We want to get the latest from source control and simply copy files out to the Drop location.

Step 3: Customizing the TFSBuild.proj

In order to make the Build Server do something other than the default we need to break open the TFSBuild.proj file. Without some kind of guidance this can be a nebulous void of XML. However, the project file is what is executed and little is hidden (even if nothing is obvious).

Here are some tips for dealing with this file:
  • There are some DO NOT EDIT things there and, probably, you can just ignore that.
  • There are some Backwards Compatibility lines in there. And unless you are using old TFS stuff then you can delete that (so it's not in the way). Most of that legacy stuff is now defined in the Build Definition rather than the TFSBuild.proj file.
  • Think of this file like a class file that is inheriting from another class. That parent class is Microsoft's build instructions (get from TFS, build, clean, test, drop, etc). You get all the default functionality and can override any part of it to do what you want
  • Remember that when you override something you must still accept the same inputs and provide the proper outputs if you want everything to work right in the overall system (for example, there is a specific action that you have to take if you want to indicate that the build process was successful. If you don't it will report failed even if you didn't have any errors).
I had trouble finding a list of all the things that I could override and what variables and things were available (MSDN has a lot of this info). Fortunately you can look at the "parent class" source. It is just another XML MSBuild project file. And your TFSBuild project file includes it right near the top.

It's path is: $(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets

This translates to c:\Program Files\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets in our environment.

Everything Team Build does is (pretty much) in that file. If you don't overwrite anything in your TFSBuild.proj file then you're looking at the code that will be executed.

There are still a lot of non-obvious things going on here. You'll see a lot of references to DesktopBuild. That is just there to throw you off. Also, since you're looking at XML and not a procedural language the execution order is not defined by the order items appear in the file.

A couple of items the differ in a TFS build vs a regular build are:
  • Entry Points
  • Variables
TFS starts by calling the Target named "EndToEndIteration". This target has several dependencies defined on it so those are executed first. In fact, EndToEndIteration doesn't actually have any code to execute. It just ensures that all its dependencies are executed in the proper order. The dependencies are listed above the Target definition (I think this is only by convention). These are the names of the several Targets that should be executed before executing the current Target.

You can override EndToEndIteration to make your own fully customized build process but since you are working in a TFS build environment you'll probably still want to execute several of the existing dependecies. InitializeBuildProperties, for example, is important as it imports a bunch of TFS settings (like paths, TFS Source Control URLS, etc) into the build so you can work from those.

Also, some activities that you will want to execute are already available. So get familiar with what is there (e.g., the "get" target pulls down the files from source control).

Defining/Overriding variables

There are two kinds of variables in these build files. The most common are defined inside PropertyGroup tags. The other kinds are called Items and I'm not sure what the difference is.

You can define your own variables like so:
<propertygroup>
<myvariable>The Value</myvariable>
<myvariable2>The Value of Var #2</myvariable2>
</propertygroup>

Later you can dereference the variables using the $() syntax:

<propertygroup>
<myvariable3>$(MyVariable) of Var #3</myvariable3>
</propertygroup>

Defining/Overriding Targets

You define (or override existing) targets using the Target tag. Your own Targets need a unique name. Override an existing target by using the existing target name. Microsoft's pre-defined targets include several that are intended to be overridden.

<target name="MyTarget">
<message text="This is my target">
</target>

A Target can hold more variables (PropertyGroup) and call other actions. Above I call the Message action. There are lots of actions available. See MSDN or Google to get some lists.

In MS' default configuration most Targets have a before and after Target you can override to do whatever you need.

Greater Customization

In our project we didn't want to build anything. We just wanted some files copied out to a development server whenever someone checked in a change. This was too outside the box to do with the default configuration.

So I ended up with the following override of EndToEndIteration to do what I wanted:

<propertygroup>
<endtoenditerationdependson>
CheckSettingsForEndToEndIteration;
InitializeBuildProperties;
InitializeEndToEndIteration;
InitializeWorkspace;
Get;
DeployWebFiles;
Messages;
</endtoenditerationdependson>
</propertygroup>
<!-- Entry point: this target is invoked on the build machine by the build agent -->
<Target Name="EndToEndIteration"
DependsOnTargets="$(EndToEndIterationDependsOn)" />


You can see that it includes several of the initialization Targets but then skips to Get and then to my own Targets.

My targets look like this:

<propertygroup>
<deploywebfilesdependson>
</propertygroup>
<Target Name="DeployWebFiles"
DependsOnTargets="$(DeployWebFilesDependsOn)">

<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Name="DeployWebFiles"
Message="Deploying Web Files">
<output taskparameter="Id" propertyname="DeployWebFilesBuildStepID">
</buildstep>

<itemgroup>
<filestocopy include="$(SolutionRoot)\NET 1.1\Trunk\Source\Web\**\*">
</itemgroup>
<Copy
SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy ->'$(DropLocation)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true"
ContinueOnError="false" />

<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Id="$(DeployWebFilesBuildStepID)"
Status="Succeeded"
/>

<SetBuildProperties
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
CompilationStatus="Succeeded"
TestStatus="Succeeded" />

<onerror executetargets="PartialSuccess">

</target>

<target name="PartialSuccess">
<BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Id="$(DeployWebFilesBuildStepID)"
Status="Failed"
/>

<SetBuildProperties
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
CompilationStatus="Failed"
TestStatus="Failed" />
</target>


I found examples of copying files through Google. I had to experiment a little to discover the file structure. You can browser the folders on your Build Agent system (remember step 1?).

Setting the right outputs

Visual Studio/TFS needs certain outputs in order to understand what is going on inside your custom targets.

I used the BuildStep action to display more steps that I can see inside Visual Studio's Build Explorer so I can watch the live progress of my build. TFS automatically does some of its own build steps but you can add as many as you want. You first create a new build step and accept its output of a Build Step ID (which you save in a variable). Then you can update that Build Step's status by passing the ID (using the $() syntax) in again.

The last tricky item is getting the build to report as successful. I finally got everything working with no errors or warnings but Visual Studio still reported the build as failed.

It looks like when you allow some of MS' default targets to run it somehow updates the build status (even though I can't find where). But, to handle this manually by calling the SetBuildProperties action.

We have to set the CompilationStatus and TestStatus to the string "Succeeded" in order to get TFS to believe that the build was successful. Again, here we are outside the box and even though we aren't doing a compilation or a test we have to report them as successful.

Drop Location

The last hiccup we had was with the Drop Location. All of MS' code turns the drop location from the one we specified in the Build Definition and adds a build path to it:

//myserver/myshare/BuildDefinitionName, Date.Number/

In our case we didn't want any of that. This meant we could either overwrite the DropBuild target or we could role our own Target. Since DropBuild is meant to deploy the OUTPUT of a build and not the source files I decided to role my own. Probably in the future we will use the DropBuild target and I didn't want to confuse other team members as to what the DropBuild functionality really is supposed to be.

The final hiccup that we haven't been able to resolve is that somewhere in the whole build process the Build folder is still generated in the Drop Location and the log file is placed inside it. I'm not sure if this is a function of the TFS Build service or one of the actions called by MS' build XML code. But it is really minor and we probably won't worry about it.

The End

I hope this helps get someone started. Unless you find a good book on the subject this initial hurdle is pretty difficult to get over. Most of the documentation/books I found were much more advanced and followed the original rule that in order to understand TFS Builds you have to first understand TFS builds.

Team Build Server (2008)

The Backstory
We have grand schemes of checking in code, having it build, test, and deploy all magically. Then we can remove developer access from the web servers and prevent out of band changes.

Unfortunately, we are missing one piece: the guy; We need the guy who can make it all happen.

It should be simple. We are a Microsoft shop with Visual Studio 2010 and Team Found Server 2008. That supports the Team Build Server and it's all designed right in. But, since we don't have the guy we've made little progress in that direction.

But, we've found another reason to get team builds in place. Our content designers (who are in a separate department) also have access to the web servers and have become our "analog hole". We've implemented change control processes and they have not. So we are working to get them on board. The biggest problem is: they use Macs and don't use Visual Studio.

We were pleased when Microsoft announced Team Foundation Server 2010 and its acquiring of the Teamprise software. Our software license would now include our Mac users and they could begin checking in to TFS. This turns out to be a real pain for them.

They use their own test web server where they build their content. When it's ready they copy the files out to the production web server. Now, in the TFS process, they have to 1) check out the files they will change, 2) copy them out to their test web server, 3) make their changes, 4) copy them back to their TFS workspace, 5) check the files back in, 6) ask our deployment guy to deploy it to Pre-production, 7) make sure it looks good in Pre 8) ask our deployment guy to deploy it to Production.

So, although we are now all synchronized and have regained control over the changes made to production we've totally given the content design team the shaft. They need to be much more dynamic that what the new process allows for.

So, the compromise is to set up the build server to automatically deploy to Pre-production when they check in content changes. They will worry about streamlining the rest of their process.

So, I became the guy who gets to set up Team Build. See the next post for details of the nightmare.

Friday, January 29, 2010

What's really wrong with the iPad

Apple has announced their iPad. Besides the stupid name and sophomoric jokes most reviews don't seem to care for the device.

What I don't get is why they don't get what the iPad is. They review it wishing the whole time it was Netbook. They want it both ways and just don't seem to get it.

You can't have the extreme ease of the iPhone and the total flexibility of a general purpose computer.

What the iPad (iPhone, iPod Touch) has going for it is:
  • Full size screen
  • Multi-touch
  • No file management (this is the bane of computer illiterates)
  • Simple application management
  • No booting
  • One piece
  • Multimedia
"Yeah, but a Netbook can do that and so much more!". Well, yeah, but a Desktop can do that and so much more. The argument doesn't hold water.

You don't need the Flash Player to experience the web. I can't believe they even argue this. The next generation browsers will be powerful enough to eliminate the need for the Flash Player in 90% of cases anyways, and mobile Safari is already on track there. It's not that far away. Any complaints along this vain really mean, "I can't watch Netflix or Hulu on the iPad." The XBox, PS3, and Wii don't have the Flash Player (at least not one worth speaking of) but they stream Netflix. If there is a market for it the iPhone/iPad will get it.

You don't need a physical keyboard to browse the web or to read eBooks. The iPhone has shown you don't need one to play games. Leaving out the keyboard keeps the form factor nice. And with multi-touch the virtual keyboard is usable.

You don't have to use eInk to be usable out doors. Besides, how much time do these reviewers really spend in full bright sunlight. You just need to turn up the brightness (more on this later).

You don't need a camera. This isn't a mobile device. It's a cordless, not a cell (phone). Perhaps a future version will have a forward facing web cam but I don't think that will fly with AT&T who seems to want you to pay for that data plan but not to use it.

You don't need a file system. This device is accessible to everyone. Mom won't lose her files because she saved them in Program Files instead of My Documents; She won't be downloading spyware or viruses; She'll always know where all her images are; She'll always know where her music and audiobooks are; She'll get the Mood Ring app and install it and run it and she'll have done it all by herself.

What the iPad missed:

Inter-application data sharing isn't limited to copy/paste. The iPad doesn't need a file system but it does need a way to email attachments from a variety of apps without each app having build out it's own "Email This" functionality. Any mp3 player I install should work with any music I've sync'd. Any ebook reader should be able to find any ebooks from any other ebook reader (among compatible formats). The Palm Pilot managed to do this more than 10 years ago. I think the iPad could swing it.

We want to print. I know the home printer world hasn't quite gone networking yet but, you're Apple, figure it out.

We want to share. I have an iPad, my wife likes her Kindle and the kid has an Android device. Make the ePub ebook format really open and let us read the books on any of our devices. Let iTunes sync with other devices even if it's only in a limited way. As much as you'd like it to be it's not an Apple world.

We need multi-tasking. Even if it's not what multi-tasking is on a Desktop. I need enough RAM (I'm looking at you iPod Touch 1g) and a UI for switching apps without exiting. Even if background apps are suspended. I want to switch between my eBook reader, email, web-browser and contact list without losing my place or waiting for the app to exit and start up again. If we can get it we want to full multitasking where that web page can keep loading while I switch over and finish composing that email.

We need a faster way to change the settings. If I do go out in the sunlight I need to be able to quickly adjust my brightness without exiting my eBook reader and going through 10 menus in the Settings app. Same goes for volume, networking and many of the other settings.

I want a mouse. The iPad supports a keyboard; We want some other peripherals. Let mice, headphones and whatever else work. This would be a boon for games and many other apps. In fact throw a CD/DVD burning device in there. And while we're at it where is the expandable storage. And internal microSD/SD card reader is a must. The Nintendo's Wii manages this and seems to handle piracy okay. You should follow suite.

I can handle the price. What I can't handle is that Amazon managed to get me free internet access for life and you didn't. I don't need another monthly bill. Convince AT&T to share my existing data plan with both my phone and iPad contributing toward my monthly limit. (Having the iPad is like having a separate electricity bill for each appliance in my house.) Give me some small amount for free and let me upgrade to the $20 or $30 plans for higher limits.


I hope the iPad finds it's niche. A lot of computing could go this closed route and I think it would be good for a lot of home users. Power users and businesses are always going to need the more open, powerful, and flexible systems we have today. But mom just can't handle it on her own. She needs an iPad (almost).

Thursday, May 14, 2009

HTML Controls in ASP.Net

The more I use ASP.Net the more I move toward (almost) plain old HTML.

For example:

<asp:Panel id="myPanel" runat="server">
I am in a Panel
</asp:Panel>
<div id="myDiv" runat="server">
I am in a div
</div>

I used to use Panels to conveniently show and hide blocks but I found that I've slowly been moving to using DIV tags with the runat="server" attribute. What's the difference? Well, with a div I get to see and control my markup. With the Panel I don't really know what is going to be rendered. I also get to apply CSS in a way that is consistent with the way I'm applying it to the rest of the page. It can be tricky to apply CSS to ASP.Net controls since I don't know what HTML they might render.

I've found that I don't need to be shy about using runat="server" on regular everyday HTML tags. I use it on TR tags for easy show/hide or server-side modification of attributes (like class or style).

I've also found that using runat="server" on input controls is often much more desirable than using the ASP.Net equivalents.

<asp:checkbox id="myCheckbox" Text="Check Me Out!" runat="server" />
<input type="checkbox" id="myInputCheckbox1" value="10" runat="server" /><asp:Label AssociatedControlID="myInputCheckbox1" runat="server">Check Me Out!</asp:Label>
<input type="checkbox" id="myInputCheckbox2" value="10" runat="server" /><label for="<%=myInputCheckbox2.ClientID%>">Check Me Out!</label>

Why use one over the other? I guess it just depends on what I want to do. The Checkbox control doesn't have a Value property and the HtmlInputCheckBox does. However, the HTML version doesn't have a Text property and I have to provide my own label. HtmlInputCheckBox gives my control over the markup but doesn't provide AutoPostBack.

This is also why I prefer the Repeater over controls like the DataList or GridView. With the Repeater I have control over the markup. And with some help libraries getting things done on the ItemDataBound event is pretty easy.

Just remember that you have options and that even in ASP.Net your run of the mill HTML tags can be empowered as well.

Tuesday, March 18, 2008

Stable Sort

.Net does not use a stable sort for its List<t> Sort method. Watch out!

A stable sort is a sort that preserves the original order for items that are considered equal.

For example, take the following list of objects:

Name, Group
John, Group 3
Sally, Group 2
Bill, Group 2
Joe, Group 3
Alf, Group 1

Now, sort these objects on the 'Group':

Alf, Group 1
Sally, Group 2
Bill, Group 2
Joe, Group 3
John, Group 3

You'll notice that the list is correctly sorted by group and the names are ignored. That is as it should be. You will also notice that Joe appears before John where in the previous list he did not. That is going to cause problems and here is why. Resort the sorted list on Group again:

Alf, Group 1
Bill, Group 2
Sally, Group 2
John, Group 3
Joe, Group 3

You'll notice that the list is correctly sorted by group but that the names have shuffled around. If you resort a third time you'll get back to the 1st sorted list. Logically you'd expect an already sorted list not to be changed by a sort algorithm. That kind of sort is called a stable sort and .Net doesn't use one. It's not a bug and it's not wrong but it may not be what you expected.

The solution is to use a stable sort algorithm by rolling your own or by sorting on unique keys. For our example we'd sort on Group then Name in order to get a determinate sort order from the unstable sort algorithm.

Tuesday, March 11, 2008

iPod Touch - 2 months later

I've had my iPod Touch for a couple of months now and things are going well. Rather than ramble I'll just skip to the lists:

Pros
  • Long battery life (compared to Pocket PCs I've had in the past). I get about 2 weeks out of it if I don't use the WiFi. This is up 700% from my last PDA
  • Snappy and good looking.
  • No stylus. Sometimes I just want to use my finger without bothering with a stylus and the Touch is designed for it.
  • Good Community. The community around the iPhone and iPod Touch is strong and using a Jailbreak I've been able to get additional applications that add significant value to the device.
  • Good WiFi. The Wifi supports all the encryption I use at home and on the road. The browser is the best mobile browser I've used.
Cons
  • Wifi sucks the batterly life away too quickly. I think this is a universal problem for any device.
  • No stylus. Sometimes a finger is not precise enough and I could really use a stylus.
  • Not responsive to touch. I often find that the device is not responsive to my touches. Especially when it's a little bit busy (loading a webpage or checking mail).
  • No speaker. I would even be happy with a crappy speaker. The clicker is not loud enough to use the Touch for alarms or reminders. And it's a pain to pull out the headphones just to watch a quick YouTube video. And headphones just don't work when I've got a couple other people that want to hear a song or see a video together. Apple is for loners I guess.
  • No mic. No digital recorder functionality. No music recognition. iPhone users get everything :(
  • iPod Touch users keep getting stiffed. The productivity apps cost extra. iPhone users get them out of the box. And now it sounds like the v2 firmware is going to cost iPod Touch users extra as well.
  • No usb mass storage. I have to carry a thumbdrive with me as well.
Indifferent
  • 8 GB Storage. I just don't listen to music or watch video on the device. I still have 7.4 GB free. It's nice to know it's there but since I can't use the thing as a mass storage device it's kind of wasted.
  • Web browsing. The browser is excellent but I like browsing the web on my 19" dual monitors.
  • Buttons. There are 2 buttons: On/Off(sleep) and Home. It'd be nice to have an external volume or pause/play but at the same time it's nice not to. There are fewer holes cut out of the leather case I got.

Firefox, Transparent Proxy and Authentication

Firefox is my browser of choice. But it does not always integrate smoothly into the office environment.

In our environment Internet access is made through a proxy server. We are required to authenticate to the server using our Active Directory credentials before access is granted.

Recently we switched over to a transparent proxy. This has been a good thing as it simplifies configuration, doesn't require changing proxy settings when I carry my laptop home, and pretty much just works automatically.

A few applications don't like authenticating to a proxy server but Firefox does very well. I am prompted for my credentials when I first open Firefox and using the Remember Password feature I usually don't have to enter the credentials but just click OK. But some things just don't work. The Search Suggest drop down causes an authentication dialog to appear (and this dialog doesn't remember my credentials) and theme, extension and automatic updates fail.

Solution

Firefox will handle authenticating proxies gracefully when configured to do so. There is no UI for setting this up but the configuration changes are easy to make using about:config. Here is how:

  1. Determine the domain name of your proxy server. You might have to ask your IT department but you can probably glean this information from the authentication dialog.

  2. Open a new tab and enter about:config in the address bar. Type negotiate in the Filter field. The preferences list should be reduced to about 5 items.

  3. Find the network.negotiate-auth.trusted-uris preference. Double click it and enter the proxy server's domain name.

  4. Find the network.negotiate-auth.delegation-uris preference. Double click it and enter the proxy server's domain name here as well.

That should do it. Firefox will automatically use your Windows credentials to authenticate to the proxy server. You should not need to make any changes when connected outside the corporate network.

Source

Monday, December 31, 2007

iPod Touch

Merry Christmas!

I've been wanting a new PDA with much longer battery life. There isn't much out there any more. PDAs seem to be on the way out. I'm not ready for a cell phone bill yet and couldn't afford most of the Smart Phone or the iPhone.

I hoped to find something that would let me go for a week without having to tether. But anything would be better than the abismal battery life of my Axim. Don't get me wrong. I like the Axim line. The x50v had a beautiful screen and all the features I could want, but if I wanted the batteries to last more than 3 hours I had to disable them all and scale back the processor speed.

So, I needed a new PDA. In college I used all the wiz-bang features but lately it's been mostly contacts and calendar. So, I've been looking for MP3 players with Calendar functions. I could not find any that allowed Calendar entry on the device. So it looked like I was going to get a Palm z22. But it's so hard to downgrade.

I found out the iPod Touch was recently updated to allow add/edit of Calendar items. It doesn't allow 3rd Party apps but I wasn't using those much anymore anways.

So, I pooled my birthday money, christmas money and the money from selling my old pda (Dell Axim v50x) to buy an iPod Touch (8GB). It's not what I expected in many ways but more than I expected in many other ways. The UI is clean and well animated. It is also confusing some times (it's different than what were used to with Windows or OSX). I'm sold on the "my finger is my stylus" idea but it's okay. I have to press a lot harder than I did on a PDA. Keyboarding is pretty good. I'd say slightly better than keyboarding with a stylus but there are no alternate entry methods.

The multi-touch seems a little gimicy and isn't used in many places. Watching YouTube and doing iTunes on the device just suck battery power. Having WiFi was one of the reasons I was leaning toward the iPod over the Palm. The Safari browser is much better than anything I've used on the PDA (I never wanted to pay for Opera).

But so far it seems pretty good. I can actually turn the thing off at night and if I keep the WiFi off (which I have a hard time doing) the battery life seems pretty good. The jury is still out on that one though.

I quickly Jailbreaked the thing and have put a few other apps on. Most aren't really useful to me. The few games available are simple but look good. Installing new apps is really simple using the Installer.app. It looks like Apple's design philosophy has been adopted by the iPod hackers.

Pros:
  • Clean UI. Very few "framerate" issues.
  • Much smaller than my previous PDAs
  • Unexpected features: Orientation detection (several games demo this), YouTube, Safari is really good. Photo browsing works well.
  • 8GB data storage
  • Syncs to Outlook
  • WiFi works well (Supports WPA2 even)
Cons:
  • Have to have iTunes to activate the iPod or you can't even use it (this should be against the law).
  • Fingers get tired (especially if you scroll through a lot of photos)
  • Finger keyboard is a little awkward since you can see the letter when your big fat finger is in the way.
  • Can't us a standard stylus even if you wanted to. The touch screen seems to be "skin" activitated
  • No Office Apps (I really like PocketExcel) or other PIM stuff (notes, todo)
  • Costs a lot for only 8GB ($300). I could get the Palm z22 and 8GB in SD cards for less than $300.
  • Body smudges a lot. I must have oily fingers
  • Firmware Updates are 150 MB+

Tuesday, October 23, 2007

SWFUpload Revision 7 beta 3

I decided I'd better get beta 3 out there. I've been too busy to test it and the demos as well as I'd have liked to so please forgive anything I've neglected. The updated demos and source have been posted in the usual place.

Changes
  • Fixed the Flash Version Detection. SWFUpload "should" properly detect Flash Player v9.0.28.
  • Fixed a left-over event call when cancelling an upload.
  • Fixed out of order parameters when calling uploadError.
  • Changed the showUI function and ui_function setting to swfUploadLoaded/swfupload_loaded_handler. This is more in line with other event callbacks.
  • Updated internal flashReady event so it is called like the other events (with setTimeout).
  • Added an internal event queue which fixes out of order events in Safari (out of order events were caused by using setTimeout to call them, just as I feared).

New Known Issues

If an upload is cancelled after the progress has reached 100% but before the upload script has finished processing then SWFUpload blocks until the upload script has completed. uploadComplete and fileComplete are still called along with uploadError. I'm still working out the best way to handle this situation (and have received some very good ideas in the comments).

To avoid this issue don't use the cancel feature or track your file progress in the JavaScript and prevent cancel calls to 100% still processing files. The issue will be fixed before the final 7.0 release.

You can test this by performing a long running process in your upload script. In PHP I tested by adding a sleep(30) statement. This causes SWFUpload to progress to 100% and then pause for 30 seconds while the upload script sleeps.

Question

I'm considering removing the ui_container and degraded_container settings. This would remove the last UI modifying bits in SWFUpload. A hands-off approach to the UI has been one of SWFUpload's design philosophies. I wondered what everyone else thinks about that.

Rather than remove them completely, however, I'd keep a default swfUploadLoaded handler that uses ui_container and degraded_container which could be stored in the customSettings object. That would keep a simple graceful degradation feature built in while better separating SWFUpload from the UI.

Monday, October 08, 2007

SWFUpload Revision 7.0 beta 2

Update: Revision 6.2 & 7 require Flash Player 9.0.28 or higher. However they fail to do proper checking to ensure that the necessary minimum Flash Player version is present. I will release an update to the R6 and R7 lines shortly.

Revision 7 beta 2

SWFUpload Revision 7.0 beta 2 is out!. I even updated the demos this time. It's all posted at the usual place: http://swfupload.praxion.co.za/. Many thanks again to our Host!

Don't forget to visit the Official SWFUpload site to show your support and to get some support.

Why Beta?

There is a bug in Flash Player that affects SWFUpload severely. It is more apparent in R7 because of the change in the Event design. I really like the new event design so I'm working hard to keep it. I'm not sure that this version is stable or how compatible it is with browsers on different operating systems. So it'll be Beta for a little while.

Changes in R7b2 from R7b1
  • I worked around an encoding bug in Flash Player where the Server Data or a file name or any string that contains a backslash (\) or a quotation mark (") in the wrong place will cause a JavaScript error which causes events to abort. Stupid Flash Player. See source for details.
  • I moved the ERROR_CODE_* constants in to "sub" objects so it's more clear what group the error belongs to (queue errors/upload errors). I changed all the error code numbers too. You didn't use any magic numbers, right?
  • Added the UPLOAD_STOPPED upload error which can supplement for an uploadStopped event.
  • Updated Flash calls and event calls with setTimeout to avoid limitations in Flash's ExternalInterface api. Stupid Flash. I haven't had any problems but watch out for concurrency and execution order issues.
  • Added a customSettings object to the SWFUpload instance so you can just set your own variables on an instance rather than using addSetting and getSetting (you can still use addSetting/getSetting if you want).
  • Changed the FILE_NOT_FOUND error code to FILE_ID_NOT_FOUND which is more precise.
  • Changed the parameter order on some events so the file object is always first in an effort to be more consistent.
Known Issues
  • You can run in to problems if, from JavaScript, you call in to Flash; which then calls back to JavaScript; who then tries to call back in to Flash. That final call in to Flash will fail. This is most apparent when calling startUpload() which calls back to uploadStart. If you try to call in to Flash from here, calling getStats() for instance, you may get an error or just a silent failure. I've work for 6 full days trying to understand and get around this. I've done pretty well by wrapping all the calls I could in setTimeouts but you can still force the bug so be careful. Stupid Flash.
  • Setting the file_post_name does not seem to work in the Linux Flash Player. It's an unnecessary feature anyway so don't use it unless you really have to (and you shouldn't have to). Thanks to Shadow Walker for the Linux bug reports.
  • You have to return something from your upload_url script or uploadComplete will never be called and your uploads will hang. I sometimes call echo ' ';
Where's the docs?

If anyone wants to write up some HTML format docs that'd be great. Otherwise I'll get to it eventually. In the meanwhile it's a good idea to skim through all the SWFUpload posts (the really old ones are still relevant). I've tried to include lots of comments in the code and between all the demos you can see how to do just about everything. Still, some docs would be nice.

Wednesday, September 19, 2007

SWFUpload Revision 7.0 Beta

I have almost completed work on Revision 7.0. I think the coding on the SWF and JS files is done. But I have not had time to convert the demos. I decided to post a "Beta" since I don't know when I'll have time to come back to the demos and documentation.

The R7 beta will be available at http://swfupload.praxion.co.za/

R7 Notes

The API has change significantly for R7 (that's why is R7 and not R65). You won't be able to drop this in over SWFUpload v1.0.2 or any of the other Revisions.

This version uses a different event model which introduced some issues with the Flash to JavaScript and JavaScript to Flash calls. I think I've resolved them but I'm sure someone will figure out a way to bring the bug up again. The long of the short of it is that when Flash calls to JavaScript if that JavaScript thread calls back in to Flash then Flash misbehaves. In order to work around this I use a setTimeout (the Timer class in ActionScript) to make calls from Flash to JavaScript. This effectively makes the call on a separate thread and works around the problem. The only place that this is not done is the call to the uploadStart event since uploadStart is required to return true or false. If you make any calls in to the Flash from uploadStart then you run the risk of triggering this issue.

Several pieces of functionality will appear to have been removed from this version. Almost everything that has been removed is still doable using the new events. There are comments in the code that explain some things and once the demos are complete hopefully you will be able to see how everything works.


Changes
  • Removed the settings: begin_upload_on_queue, validate_files, and use_server_data_event.
  • Changed the setting: upload_target_url to upload_url.
  • The events called are as follows and are listed in their approximate order:
    • fileDialogStart: Called from Flash immediately before the File Dialog is displayed.
    • fileQueued: Called once for each file that is added to the queue.
    • fileQueueError: Called once for each file that fails to be added to the queue.
    • fileDialogComplete: Called after all selected files have been processed.
    • uploadStart: Called for each file in the queue immediately before it is uploaded. Return true to continue the upload. Return false to skip the upload.
    • uploadProgress: Called periodically to provide upload status for the current file.
    • uploadError: Called when an error occurs on an upload or if an upload is cancelled.
    • uploadComplete: Called when an upload finishes successfully.
    • fileComplete: Called after an upload and SWFUpload is ready to allow another upload to begin. This is called even if an upload is cancelled or has an error.
  • Removed the queueComplete event and the cancelQueue command.
  • Enhanced the cancelUpload command so if it is not passed a File ID then the current upload or first file in the queue is cancelled.
  • File cancelling behavior has changed. If an in-progress upload is cancelled the uploadError event is fired and the fileComplete event is fired. If the file is not currently uploading then only the uploadError event is called.
  • Added a GetStats command. This returns an object that contains the number of successful uploads, the number of files remaining in the queue (excluding the current upload, if any), the number of queue errors and the number of upload errors.
  • Added a file_status property to the File Object.
  • Added a single file browser. This allows the user to only select a single file for upload.
Some of the Command names, Event names, and Error code constants have changed. See the source code for the updated names.

Tuesday, July 10, 2007

SWFUpload Revision 6.2 Release

So, here I am releasing a new version even though I said I wasn't going to. Some bugs were fixed that warranted a version change and forced me to change some functionality.

Revision 6.2
  • Fixed the Type Coercion error
  • Fixed FileComplete not firing
  • Added setting that indicates whether FileComplete or ServerData should fire (both call the FileComplete JavaScript handler)
  • Fixed a race condition (see R6 post)
  • Added API for changing all the changeable settings dynamically (file type, upload limits, etc)
  • Updated Classic Forms demo to use Server Data.
  • Updated Features demo to reflect API/feature changes
Notes:

The website has been updated. Get the latest greatest there. And as always let me know if you come across any bugs. A big thanks to all those who reported bugs in R6.

I've been concerned that people are downloading the Revisions and attempting to plop the demo code in to their own websites or apps. I strongly recommend against doing this. The Demo code is poorly designed, poorly test and is insecure. Furthermore, my PHP is very rusty and out of date.

The purpose of the demos is to give you working examples so you can see how things work since I haven't produced any documentation yet. But they are like the snippets from "Learn X in 21 days" books. They are there to get a particular point across but they do things wrong for the sake of simplicity.

As another disclaimer: I am an amatuer Flash/ActionScript developer and much of the code is derived from short examples taken from the LiveDocs. The code may be insecure and done wrong because I am guilty of using example code in to the Revisions.

So we are all getting what we pay for.

Tuesday, July 03, 2007

SWFUpload Revision 6 Release

Update 7-5-2007

A couple issues were found in Revision 6. The major one was another race condition that could occur in IE6. Hopefully these race condition bugs have finally been squashed. A few other issues were found but were not noticeable since they kind of canceled each other out.

I've posted updated Demos and Source code. I'm not updating the version number so if you downloaded a copy before today I recommend that you download the updated copy.

SWFUpload Revision 6 Release

I am pleased to announce the release of Revision 6 of SWFUpload. This version has been moved forward to ActionScript 3.0 and Flash Player 9 which gives us some power features what everyone has been waiting for.

What's new:
  • Code moved to ActionScript 3.0.
  • Flash Player 9 required.
  • Added ability to send custom POST values.
  • Added a fileValidation event that allows each file to be validated before upload.
  • The fileComplete event receives the output from the server.
  • File type (extension) is checked again in code since the file browser can be bypassed.
  • The file post variable name (aka Filedata) can now be changed.
  • Fixed a bug caused by canceling single files.
  • Removed the cookie sending feature. The post_params setting can be used.
  • Removed query_params. Use post_params instead.
  • Fixed another race condition that could prevent SWFUpload from loading on a page refresh.
  • Updated PHP and ASP.Net demos.
How to use it

See the demos and download Revision 6 at our generously hosted site (thanks again!). I have had some trouble with browsers/proxies caching some of the old JavaScript files. If it doesn't work make sure your caches are cleared.

My next project is to produce documentation for SWFUpload. I don't plan any new features (unless Flash Player 10 fixes/adds cool features) except bug fixes.

Don't forget to show your support to the original SWFUpload developers. We're grateful for their innovation and hard work

Monday, June 18, 2007

Checkboxes in Repeaters (ASP.Net 2.0)

Every 3 or 4 months I build a website that uses a list of check boxes to allow the users to make a selection from a list of items.

Every 3 or 4 months I fight, curse, and swear at ASP.Net and its databound controls (okay, I don't curse or swear but I do fight).

So, I've just finished doing it all again and this time I'm going to write it down so I don't forget how it's "supposed to be done".

Scenario

I have a list of thingies that I want the user to choose from. I list the thingies out in a repeating control (I use the asp:repeater but a asp:datalist behaves the same).

The user checks off the items they want and clicks a button firing off a server side onClick event.

The Problem

You loop through all the check box controls and none of them are checked. A secondary problem is that the asp:checkbox control does not have a "Value" property to store useful information about what the checkbox might be tied to.

"Wrong" Solutions

After searching the Google for help and only finding posts from 2003 I figured that either everyone gave up or this isn't really a problem for anyone but me.

One search hit suggested that I make sure I DataBind my repeater on every post back (in other words, don't put my data binding inside the !Page.IsPostback block of my Page_Load). This didn't work.

Someone suggested creating a hidden control to store a Value (since asp:checkbox doesn't have a value property), but doesn't really seem to address the issue of all the checkbox check states missing when you post back.

I found a suggestion that said making the itemcommand event trigger and then looping through the all items on the repeater would work. I think this method would allow you to get the dataitem associated with the checkbox and might preserve the check states. However, I wasn't able to get something workable using this technique. And it feels a bit wrong to use itemcommand which is intended for a single "row" or item and apply it for the entire repeater.

"Right" Solution

So the solution I found is to use the HtmlInputCheckbox control instead of the asp:Checkbox control. It really is that simple. It has a Value property and it maintains its checked value after the postback.

To retrieve the check states and values simply loop through each item in the repeater and get the html input check box using FindControl. Do this before re-binding the repeater.

Example (ASP.Net 2.0)

HTML (test.aspx)

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<h2>Which are your favorite Beatles?</h2>
<table cellspacing="1">
<tr >
<td>&nbsp;</td>
<td>Name</td>
</tr>
<asp:Repeater ID="repList" runat="server">
<ItemTemplate>
<tr>
<td>
<input type="checkbox" id="cbItem" runat="server" />
</td>
<td>
<asp:Literal ID="litName" runat="server" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
<p><asp:button ID="btnVote" text="Vote" onclick="btnVote_Click" runat="server" /></p>
<p><asp:Label ID="labResult" runat="server" /></p>
</form>
</body>
</html>


Codebehind (test.aspx.cs)

using System;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;

public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.repList.ItemDataBound += new RepeaterItemEventHandler(repList_ItemDataBound);

// Load the class data
if (!Page.IsPostBack)
{
this.BindRepeater();
}
}

protected void BindRepeater()
{
List<ListItem> items = new List<ListItem>();
items.Add(new ListItem("John", "1"));
items.Add(new ListItem("Paul", "2"));
items.Add(new ListItem("George", "3"));
items.Add(new ListItem("Ringo", "4"));

this.repList.DataSource = items;
this.repList.DataBind();
}

protected void btnVote_Click(object sender, EventArgs e)
{
string vote_results = string.Empty;

foreach (RepeaterItem ri in this.repList.Items)
{
HtmlInputCheckBox hcb = ri.FindControl("cbItem") as HtmlInputCheckBox;
if (hcb != null)
{
if (hcb.Checked)
{
if (hcb != null)
{
int id = Convert.ToInt32(hcb.Value);
vote_results += id + " ";
}
}
}
}

this.labResult.Text = "You voted for " + vote_results;
this.BindRepeater();
}

protected void repList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
ListItem item = e.Item.DataItem as ListItem;

if (item == null) return;

Literal litName = e.Item.FindControl("litName") as Literal;
HtmlInputCheckBox cbItem = e.Item.FindControl("cbItem") as HtmlInputCheckBox;

litName.Text = item.Text;
cbItem.Value = item.Value;
}
}

Friday, May 11, 2007

Revision 5.2 update

I have posted an update to SWFUpload Revision 5.2. It does not add new features so I decided to keep the version number. Several function names have changed, however, so you can't just drop in the new swfupload.swf and swfupload.js files.

Changes
  • Added the Application Demo which demonstrates a working upload script and dealing with passing PHP Session IDs to the upload script as a work-around to the Flash/Firefox cookie issue.
  • Restructured the JavaScript code to comply with YUI style suggestions and to make a clean pass at www.jslint.com. This makes for better less error prone code.
    • The major change here is changing all function names to camelCase.
    • Moved the Cookie and Console objects inside the SWFUpload object.
  • Cleaned up some additional logic
  • Created a function this.DebugMessage(msg) to replace all the if (this.debug) Console.Writeln(msg);. Which does the same thing but gets ride of the all ifs and allows for easier changes to debugging in the future.
  • Added a ChangeLog.txt file and removed the 100 lines of comments from the top of the swfupload.js file.
Hopefully these changes will improve my code consistency, make the code easier to read, and make it more compatible with other JS libraries.

I recommend using the full blown swfupload.js for development but then using jsmin to reduce the file from 27 KB to 16 KB.

The demo swfupload.swf also has not had any of the "This script is running too long" patches applied to it. I have not, personally, had any of these errors come up but you may want to apply the patches anyways.

I've decided it's time to learn JavaScript and have started with the resource at http://javascript.crockford.com/ which are very good and reasonable. Hopefully my understanding and coding of JavaScript will improve ;)

Friday, May 04, 2007

HTML <button> tag

I am frequently amazed at the new "old things" I discover as I continue in web development. For example, I'm coming around to using the H tags and the P tag for markup. I've always used spans, divs, and brs.

My most recent "discovery" is the button tag. This little jewel has its problems but it gives me some of the flexibility that an input type="button" just doesn't provide.

See for yourself:



The <button> tag accepts HTML content rather than a simple text value. As far as I know (I don't know much) you can put most anything in there.

It is important to remember that <button> has a type attribute that determines whether it behaves as a submit, reset, or simple button.

Some caveats:

IE does not handle form submission of <button>s correctly.
  • Rather than submitting the value (as specified in the value attribute) it submits the inner HTML of the tag.
  • IE submits all the <button> tags; not just the button that was clicked. This makes it so the server-side script is unable to tell which button was clicked.

Work-arounds:
  • Set the type attribute to button and use JavaScript. If you can't use JavaScript then I'm afraid you may not have much success with the <button> tag (especially if you want to use more than one <button> tag in a form)
  • Button Value: Use the JavaScript onclick event to populate a hidden field with the value from the button. In your server-side scripts you can read out values from the hidden fields and ignore the buttons.
  • Which Button Clicked: Use the JavaScript onclick event to populate a hidden field with a value that identifies which button was clicked. On the server-side use the hidden field to determine action rather than the button.
ASP.Net already uses similar methods for its PostBacks and so these work-arounds aren't necessary. Just remember to set the type attribute to button or you will get two postbacks when a button is clicked. Bind click events in ASP.Net by using the onserverclick event.

Thursday, May 03, 2007

SWFUpload Revision 5.2 Release

Edit: If you downloaded R5.2 before May 4th you should download again. I made some minor fixes that I missed:
  • Zero file size limit fixed to mean unlimited
  • Default flash width/height set to 1px to work better in Linux (also default color set to #FFFFFF since it is visible in Linux.
/Edit

Alright. I've completed another overhaul and feature creep cycle in the SWFUpload Revisions. I'm going to have to take a break for awhile. It's time to get back to work (real work I mean).

Revision 5.2 includes updates to several features, improves the internal processing of the files and takes care of some bugs that I missed because of insufficient testing.

Changes
  • Out of Order file uploads now possible.
  • Per file upload parameters (these are added to the upload target query string. These are also passed to JavaScript with the fileObj).
  • Dynamic modification of cookie and custom parameters (fixed so cookies aren't wiped out when updating other settings).
  • Added a Queue Limit. E.g. Use if you only want users to queue and upload one file at a time you can set the queue limit to 1.
  • Added a QueueStopped event (change from throwing a FileQueued event when the upload was stopped).
  • Fixed the various limits so setting them to zero means unlimited.
  • Added a ui_function setting that allows you to specify the function to call instead of _showUI. This gives you the power to do whatever you want with the UI after SWFUpload has completed loading without having to modify the SWFUpload javascript files.
  • Created a "Features" demo that lets you tweak (almost) all the settings in SWFUpload R5.2 and test it without having to fiddle in the code.
  • ActionScript modifications:
    • Changed from using an ever growing array to track each queued file to a FIFO queue. This simplifies the code in a lot of ways.
    • Wrapped the FileReference object in a custom FileItem object which allows per file parameters and better abstraction.

The Revisions website has been update: http://swfupload.praxion.co.za/index.php

Another big thanks to Tarkheena for providing the great hosting!

Additional Notes

I've been having all kinds of trouble with UTF-8 encoding and byte headers. I tried to get the encoding consistent but may have missed some files. Incorrect encoding has caused issues ranging from PHP errors to JavaScript files not loading. Let me know if there are any problems.

There was a report that SWFUpload R5 (and probably R5.2) does not work under Firefox 1.5 in Linux. I plan to test and hopefully fix this soon.

I tested R5.2 in Ubuntu 6.10 on Firefox 2.0. I fixed a couple small issues that prevented SWFUpload from loading (see Edit above). I noted that it seems like FileProgress events are not called very often. Also, Stopping or Cancelling causes a crash. If you are targeting Linux users you should include JavaScript that detects the Linux Flash Player and prevents these actions. I don't plan to add this kind of detection to my code.

I can create a browser crash in both IE and Firefox on Windows XP SP2. While I have the File Dialog open, I force a page change (reload or move to a different url). I happened on this when I was using <input type="submit" onclick="suo.Browse()">. The File Dialog would open but then the form would submit and change pages. The browser would crash as soon as I clicked Open or Cancel.

Tuesday, April 24, 2007

SWFUpload R5 Release!

I'm happy to announce the release of SWFUpload R5 (my heavily modified version of the original SWFUpload).

I'm also pleased to announce that Tarkheena has graciously provided hosting for the SWFUpload R5 demos and downloads.

And a final thanks to Lars and the SWFUpload team. They have created a great upload control and it's been great expanding on their work.

SWFUpload R5 New Features and Changes
  • Tested and Fixed the automatic cookie passing feature. It works now.
  • Switched to PHP for the demo format.
  • Switched from Flash Studio 8 to FlashDevelop. This is an Open Source Flash IDE that works great. I seem to have been able to work around the bugs found when SWFUpload v1.0.2 moved to FlashDevelop. This move allows anyone to modify the SWF file. Not just people who can afford Adobe's Flash Studio.
  • Greatly improved the callback system. You no longer pass in a string with the function name to the settings. Now you pass in the actual Function reference. This gets rid of the flashReady_callback handler, which always felt wrong, and makes more advanced things possible with multiple instances. This also allow the this object to refer to the SWFUpload instance inside the Event Handlers.
  • Renamed just about everything to make the settings and functions more consistent and clear. Replaced the -XX error codes with names and other such niceties.
  • File Selecting is now subject to the FileUploadLimit. E.g., if the upload limit is 10 and the user attempts to queue 11 files then an error is thrown. Or if the file has queued 10 files and attempts to queue 1 more then an error is thrown.
  • Returns a sensible error when Zero Byte files are uploaded.
  • You can change the UploadTargetURL at anytime using the SetUploadTargetURL function.
  • Improved the SWF to Javascript debugging.
  • Improved the encoding of strings.
Demos and Download

You can try out the demos and download the source at http://swfupload.praxion.co.za/index.php

Please note: there is a 2MB upload limit on this server regardless of what SWFUpload allows. I'll see if I can get this fixed soon