Tuesday, February 12, 2008

just released trycaptcha - a captcha directory

Having a hard time deciding which CAPTCHA to use on your site?

We did.

After trying out a number of captchas we decided to make a directory, and save you the time. You can find it at www.trycaptcha.com - and when you go there you can:
  • See what the different captchas look like
  • Try out the different captchas. (yes, much better than just a list ;) )
  • Discuss the different captchas' pros and cons


At the moment these are all open-source/free PHP captchas, and we might extend it in the near future, as well as let you also grade the different captchas, if people show an interest.

Well, go on and knock yourself out with those CAPTCHAS!

And for those of you who aren't familiar with captchas,
here's what I got when searching google for "define: captcha":

Definitions of captcha on the Web:

  • An image containing a numerical or alphabetic code that can normally only be read and interpreted by a human. ...
    www.leapinghare.co.uk/help/glossary.html

  • An image displaying a message along with lines or shades that make it hard for a computer to do character recognition. Used in WebGUI to prevent accounts from being created by programs or spiders.
    www.plainblack.com/community-wiki/webgui-glossary

  • Completely Automated Public Turing test to tell Computers and Humans Apart. A test that is designed to be easy for a human to do, but be difficult to automate. An example of a captcha is the practice of displaying distorted letters and asking the user to type them into a form. ...
    www.nemesys.com/Content/Core/Glossary.php

  • A CAPTCHA (stands for "Completely Automated Public Turing test to tell Computers and Humans Apart", trademarked by Carnegie Mellon University) is a type of challenge-response test used in computing to determine whether or not the user is human. ...
    en.wikipedia.org/wiki/CAPTCHA

Saturday, January 19, 2008

Site performance - javascript & css aggregation, and CSS sprites

About a month ago we started working hard on improving the performance of our Traxtuff website. After some research we came to the conclusion that it was our many css and javascript files that were causing most of the performance problems. Also - every css and javascript file were called many times on each page, causing very strange firebug problems. So we decided to add a javascript and css aggregation mechanism to our project.

Basically how it should work is by replacing all the calls to javascript and css by one call to a file containing all the concatenated javascript and css files. It should recognize when the cache is out of date and regenerate it. It should include each javascript and css only once inside of the cache.

Now, I am very loath to write "frameworks". Why? Because I would much rather find someone else's framework and use it. Most likely they already went through all the debugging and testing of the framework. Still, I couldn't find someone else's php code that does this. Drupal 5 has css aggregation, and Drupal 6 has css and js aggregation, but since we don't use Drupal in Traxtuff, I wanted to write clean code that would do this for me.

So after a long and arduous debugging process where almost every day I received an emotional email from Yonatan about another bug in the caching mechanism, I finished writing our very own javascript and css aggregator for PHP (Amazing how many bugs 175 lines of code can contain).

I released it in google code here. There are many problems with this project, it is not really a "framework" yet, but more ugly patchy code. But I feel it is good enough to let other programmers look at, and maybe copy paste relevant portions into their projects. I hope to "componentize" this code in the near future.

I can safely say our site now loads about 5 times faster than it did before we introduced this caching mechanism.

However, as our site's design gets more complex, we bumped into another problem - the site is starting to slow down again because of having too many images (many of them quite small). The best solution for this kind of problem is to use sprites (basically a technique of slapping all of your images into one large image and using CSS to display only the appropriate portions). You can read about it in A List Apart. We haven't done this yet, but I found a really cool sprite and css generator we can use.

To learn about improving your site's performance, I really recommend reading Yahoo's best practices for speeding up your website, and also installing the really really cool YSlow firefox plugin to check how well you applied those practices to your site.

Friday, January 18, 2008

hurrah Modalbox

I needed a modal dialog for Traxtuff (www.traxtuff.com). I've searched far and wide, and came up with this one:
http://www.wildbit.com/labs/modalbox/
aka "Andrew Okonetchnikov's modalbox".
I found some other good stuff like ThickBox, but I use Prototype and Scriptaculous already and Thickbox is based on jQuery. jQurey is great. Prototype/Scriptaculous is also great.
there's also "Lightbox" and "Lightbox gone wild" (you can see links to others on the modalBox page).
for me modalbox worked great, and since it made such a good impression I am considering also adopting their tooltips lib.
browsing around their site I read the blog and was also happy to find some interesting content and almost no BS :)
http://www.wildbit.com/blog
so, I'll keep you posted if I have any trouble or need to tweak it. so far it worked great out of the box.

UPDATE 25/1:
I ran across some encoding problem in IE7, (didn't test on IE6) - I don't know if it's in modalBox, but I will describe my solution here anyway. if anybody can shed more light on the subject please do.
so: I use modalBox to do invitation previews. before inviting users to the site the users see a preview of the invitation about to be sent. I used modalBox to display the preview - just pass the invitation preview URL + a couple of parameters to modalBox and it does the rest very nicely.
the problem I had was that the personal message, which I also sent by GET, appeared garbled in the preview modalbox. this only happened on IE7.
I added the line
header('Content-type: text/html;charset=UTF-8');
to the preview code, hoping it's a header issue, but no such luck.
then I decided to try another approach and just base64ed the message field and decoded it in the preview script. this solved the problem.
shame on me, I didn't dive deeper inside to find the reason for this. but if you know it or have suggestions - thanks in advance.

Tuesday, January 15, 2008

Gettext for PHP and Javascript

I feel like a spy returning from the cold. I haven't visited this blog in quite a few months, mostly because writing blogs is difficult. I must say after writing my first 2 posts I have a whole new appreciation for bloggers our there, who I mocked and dissed over years. But as a web programmer I bump into weird problems almost every week, and after I solve them I always tell myself "someone should write about this." So I finally picked up the courage to return here again. But enough of that lets get to business.

Recently we have been internationalizing (aka i18n, l10n) our website Traxtuff. Like most programmers we began by producing a detailed design of our dream translation framework for PHP, but a quick search in google made us choose gettext. There are many good articles about using gettext with PHP(Here's one and here's another, Pablo Hoch also posted a benchmark of gettext). Even though it's far from perfect, it's widely used in many projects, so we decided to use it. We added some nice helper functions to wrap _() (aka gettext()):


function _L($str)
{
echo _($str);
}

function _S()
{
if (func_num_args() == 0) {return;}
if (func_num_args() == 1) {_L(func_get_arg(0));return;}
$args = func_get_args();
$args[0] = _(func_get_arg(0));
echo call_user_func_array('sprintf', $args);
}

function _T()
{
if (func_num_args() == 0) {return false;}
if (func_num_args() == 1) {return _(func_get_arg(0));}
$args = func_get_args();
$args[0] = _(func_get_arg(0));
return call_user_func_array('sprintf', $args);
}


Don't ask what L, S & T stand for, I just chose letters randomly :) .

We then had to decide what to do with internationalized strings inside our javascript files. I bumped (completely by accident while researching something else) upon a cool implementation of gettext for Javascript. Here is a link to it. I admit it's not very well documented (major understatement), but it's pretty simple to use. To get the code you need to go into the code repository here. This code depends upon the ubiquitous prototype javascript framework. Basically what you need to do is add in your HTML a link to your po (POT) file:

<link rel="gettext" lang="de" href="[path to your po file]"></link>

And after it in your scripts:
Gettext.lang = 'de';
Gettext.gettext('hello %s','world');
_('hello %s','world');


Yonatan couldn't make this work, so he added before this the code:

Gettext.include.apply(Gettext, Gettext.links[Gettext.linksPointer]);
Gettext.linksPointer++;

Hope this works for you. Let us know if you find something more elegant.

Everything was working peachy, until we upgraded to prototype 1.6. I spent a few hours today trying to figure out why gettext.js stopped working. Finally it turned out that the prototype function Array.indexOf doesn't work in the same way (hope I'm not saying something stupid). Previously it supported finding elements in an array like this:

var arr = [["a"], ["b"]];
arr.indexOf("a");


But now it only supported flat arrays (I think this is because prototype.js first checks if the browser already has an implementation of Array.indexOf). So I fixed gettext.js by adding flatten() call before calling indexOf for the message arrays. It's just a small change in 2 places, so it's not too scary :).

That's all, happy hacking for all of you.

p.s. We recommend creating a separate po files for your javascripts, so it will be as small as possible. After all this gettext implementation isn't very efficient.

Tuesday, August 28, 2007

Tolerance to partial data

Yonatan and I were discussing one of our new projects today, when we had an interesting realization. One of the primary differences between the old desktop application approach and the newer "web 2.0" approach is the application's "tolerance to partial data".

In the old days, the approach was "all or nothing". Programmers would assume that the user will have patience to provide the application with a complete set of details. The application designers assumed that their application lives in a perfect world where all required data is available, and constructed their algorithms accordingly. If the user entered all the required details, she'd get a result. But the application wouldn't output anything meaningful before all the right data was entered. Bad applications would crash. Better applications would ask politely for the missing details, or provide some graphical hint that further action is required. It's not surprising that most applications were (or actually - still are) built this way, because programmers are really really used to this (e.g., try forgetting a } somewhere in your code...).



New applications - especially web applications - require a different approach. Most of today's computer users don't have the patience to provide the application with all the data it needs for a perfect result. People want results quickly, and they don't want to enter a lot of data. The good news is that people will often settle for less-than-perfect results in such cases. This means that the application can provide some of the functionality in return for some of the data. If the user really wants full results, the user will enter all the data. But the application shouldn't make this mandatory.



Why has this become especially important with web applications? Because many applications are "nice to have" - they're not crucial. In the days of the desktop, computers were mostly used for business-related applications. They were critical to the operation of the business, and people were willing to invest a lot of time providing them with data. But the web has made it possible to provide users with applications that are neat - but not critical. If users don't get to see some results quickly - they'll move over to another web site. For a "nice to have" application to be successful - it must be tolerant to partial data.

Thursday, March 8, 2007

Selenium IDE - format for CakePHP Selenium helper

This is a companion post to Daniel Hofstetter's (author of cakebaker blog), about cakephp selenium helper.

OpenQA Selenium Core is a testing framework for web applications.
Cakephp is a MVC framework for php.
SeleniumIDE is a firefox extension that allows recording and running of selenium test cases within the browser.

The Cakephp Selenium helper allows writing test cases for selenium in php code, instead of the annoying html table style "selenese". I was using it for a while before I tried out the SeleniumIDE firefox extension. I found out that it had an export capability that created code for different languages like ruby and perl, but no cakephp :(. Luckily writing a new formatter is fairly easy.

Here is the code for an implementation of a cakephp formatter. It's not perfect, and doesn't add a line for creating the testCase name, but that should be pretty easy to add:


/**
* Parse source and update TestCase. Throw an exception if any error occurs.
*
* @param testCase TestCase to update
* @param source The source to parse
*/

function parse(testCase, source) {
}

/**
* Format TestCase and return the source.
*
* @param testCase TestCase to format
* @param name The name of the test case, if any. It may be used to
embed title into the source.
*/

function format(testCase, name) {
var result = '<?php\n';

var commands = testCase.commands;
result += formatCommands(commands);
result += '?>';

return result;
}

/**
* Format an array of commands to the snippet of source.
* Used to copy the source into the clipboard.
*
* @param The array of commands to sort.
*/

function formatCommands(commands) {
var result = '';
for (var i = 0; i < commands.length; i++) {
var command = commands[i];
if (command.type == 'command') {
if (command.value !== "") {
result += '$selenium->' + command.command
+ '(\'' + command.target + '\',\'' + command.value + '\');\n';
}
else {
result += '$selenium->' + command.command
+
'(\'' + command.target + '\');\n';
}
}
}

return result;
}

/*
* Optional: The customizable option that can be used in
format/parse functions.
*/

//options = {nameOfTheOption: 'The Default Value'}

/*
* Optional: XUL XML String for the UI of the options dialog
*/

//configForm = '<textbox id="options_nameOfTheOption"/>'


To add a formatter to SeleniumIDE follow the following tutorial:

1. Open the SeleniumIDE extension, and choose options in the menu


2. Click the "add" button


3. Paste the code presented above in the textarea


4. Record your test, and then you can choose "File/Export Test As/cakephp"


And something like


$selenium->open('/');
$selenium->clickAndWait('link=test problem');
$selenium->clickAndWait('img_avatar');
$selenium->clickAndWait('sign_in');
$selenium->type('UserUsername','bla');
$selenium->type('PasswordPassword','bla');
$selenium->click('rememberme');
$selenium->clickAndWait('submit');
?>


is generated.

If you would like to open an existing cakephp test in SeleniumIDE, you don't need to write a parser, just "open" the url for your test, for example "http://localhost/pages/tests/test1" and your testcase will generate the table that is parsed by SeleniumIDE, you can make your changes, and export it back to cakephp.



Tags: ,

Selenium Core 0.8.2 testing framework - fixing bug of pause/continue button not working properly

I was using this nifty web application testing framework (opensource) for a while before the following bug became intolerable, and I searched for a solution on google. Hopefully this blog post will save you some trouble:

After pressing the pause button, pressing continue doesn't actually resume the run, but only does a single "step" and stops again!

How to solve this bug? Edit the file /scripts/selenium-testrunner.js and find the function reset extending the object HtmlTestRunnerControlPanel, around line 345.

Remove the comment on the line:

// this.runInterval = this.speedController.value;

=>

this.runInterval = this.speedController.value;



Voila! Pressing continue should now run smoothly.

p.s.

1. Apparently they already fixed this in the trunk:
http://svn.openqa.org/fisheye/browse/selenium/trunk/code/javascript/core/scripts/selenium-testrunner.js?r1=1690&r2=1742

2. This is the entry in the openqa forum where I found the solution: http://forums.openqa.org/thread.jspa?threadID=6163

Tags: