Thursday, 30 July 2015

When to avoid AJAX

I love AJAX. I really do.

It's a tool that has brought me countless joy even as I built complicated pages that require tons of functionality. It can be great for HCI (Human-computer Interaction), and the like. But like all tools, it can be overused. And misused. I know from first-hand experience.

A supervisor once sent me a memo to "remove all unnecessary AJAX". As far as I was concerned, that would entail removing just about every occurrence of AJAX. Because very little about AJAX is "necessary". Most things work fine without AJAX. AJAX just adds on certain little perks - like  not needing to reload your entire page to get new information. Your application isn't going to die without it.

So here are some of the situations where you should seriously consider not using AJAX.


Having a hard
time searching?

 

You need your stuff to be search-friendly

Search robots trawl HTML that has been generated by web servers. AJAX bypasses the HTML generation process, filling in content in the HTML placeholders only after the HTML has already been generated by the web server. As such, search engines are only able to pick up the HTML of the pages before the AJAX-generated content has been filled in.

If, for example, you're running a news blog and your articles are generated by AJAX, all robots are going to pick up are the generic HTML templates that's used to house your AJAX content. Kind of like getting the donut without the jelly. Hardly ideal if you're going for Search Engine Optimization and a good web presence. Not a concern if it's an in-house web application..


The conventional way,
and the newfangled way.

 

The conventional way works just fine, or better

AJAX, by design, breaks up the traditional flow of the web. Back, Forward and Refresh buttons do not work in expected ways. That can be a good thing, and it can be a terrible thing. Sometimes, you need your pages to be synchronous.

Let's say you have a very long article that has been broken down into 5 different pages. Being able to use the browser's Back and Forward buttons on this one, would be invaluable, but impossible if the 5 pages of content are served by AJAX. And bookmarking each page would be an exercise in futility because the bookmarks would all point to the same URL.

By the same token, you may want to avoid AJAX if your page lacks refreshable content. By "refreshable" content, I mean dynamic content that needs to be updated periodically while the user is accessing the page. This may apply to news feeds, user-posted comments and user notifications. It makes no sense to apply AJAX to a page with only a header, content body and footer. Try to avoid overkill. Remember, at some point, people are going to have to maintain the page.

The entire point is to give users what they want with a minimum of fuss.


Does stuff need to run on the
older platforms?

 

Backwards-compatibility is an issue

If JavaScript is disabled or very old browsers are being used, there goes your AJAX. There are, of course, times when I'm tempted to say "to hell with it" and do it anyway. But understand that you're taking a very real risk here of alienating that portion of your intended audience. If your content can be served just fine without AJAX and you really need as many people to see it as possible, go retro.

A good example would be pages with content that does not need to be dynamic and isn't expected to change very often, such as a technical document.


Remember this!

Being the exciting tool that it is, developers get tempted to use AJAX just because they can. And that is usually a bad idea. There are no blanket solutions in this business - not AJAX, not nothing.

Don't overdo anything. Don't be A-JAX-ass.
T___T

Saturday, 25 July 2015

An Eventful JavaScript

Cross-browser compatibility can be a real pain. Front-end specialists, especially those who aren't making use of some framework or other, can attest to this. The inconsistencies from one browser to another are numerous - sometimes almost overwhelming.

In fact, today I want to talk about one such inconsistency - the Event object. The Event object is what holds information about JavaScript events such as onload, mouseover and so on. For a complete technical reference, follow this link.

We've used Event objects during the web tutorials. And one thing you will find when I handle JavaScript events, is that I mostly have this particular snippet:
if (!e)
{
    var e=window.event;
}


What does this do, and why is it necessary?

Glad you asked! It first checks for an event variable, and if there isn't one, it explicitly creates one.

As for why it's necessary, check out this code snippet.
<script>
document.onmousedown=mousedown;

function mousedown()
{
    alert("x:" + event.clientX + " y:" + event.clientY);
}
</script>


You can copy and paste this into a HTML file, then run it in Chrome. What happens. When you click, does it give you the x and y coordinates of your mouse cursor?

Cool. Now run it in Firefox. Not a peep, right?

That's because in WebKit-based browsers such as Chrome and Safari, event is stored as a Global variable. This isn't true for Firefox, and that's why the JavaScript in Firefox can't detect the Event object.

Here's a list of WebKit-based browsers.

Here's the tweak...
<script>
document.onmousedown=mousedown;

function mousedown(e)
{
   if (!e)
   {
        var e=window.event;
   }

    alert("x:" + e.clientX + " y:" + e.clientY);
}
</script>


Now this will make it work on Firefox in addition to Chrome.

That's all I have for now. Any... objections?
T___T




Tuesday, 21 July 2015

Web Tutorial: The CAPTCHA (Part 4/4)

And finally. We're going to distort the image that your tt_getcaptcha.php script sends to your front-end.

Usually this involves a graphics library, but that's too much like work. Instead, I'm going to generate three sets of digits from 0 to 9, using CSS shapes. You may recall that earlier this year, I put up a web tutorial detailing how to use CSS to simulate Chinese brush strokes. Well, we're going to use the same techniques here. This CSS code will be written right into the PHP script.

Confused yet? Or intimidated? Don't be. It'll all become clear presently. Open your tt_getcaptcha.php file, and add the following code to your existing code.

tt_getcaptcha.php
<?php
session_start();

$numbers=array();

for ($i=0;$i<=9;$i++)
{
    $numbers[$i]=array();

    for ($j=0;$j<=2;$j++)
    {
        $numbers[$i][$j]=array();
    }   
}


$_SESSION["captcha"]="";

for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;
}

echo $_SESSION["captcha"];
?>


Here, I have 3 sets of numbers for 0 to 9. So I'm going to use a multi-dimensional array $numbers to store them. So I declare the number of numbers (0 to 9) as one dimension, Each element, from 0 to 9, has 3 variations, from 0 to 2. Here's a tabular representation of the array below.

Set 0 Set 1 Set 2
$numbers[0] [0][0] [0][1] [0][2]
$numbers[1] [1][0] [1][1] [1][2]
$numbers[2] [2][0] [2][1] [2][2]
$numbers[3] [3][0] [3][1] [3][2]
$numbers[4] [4][0] [4][1] [4][2]
$numbers[5] [5][0] [5][1] [5][2]
$numbers[6] [6][0] [6][1] [6][2]
$numbers[7] [7][0] [7][1] [7][2]
$numbers[8] [8][0] [8][1] [8][2]
$numbers[9] [9][0] [9][1] [9][2]

Now, each number of each set requires a certain number of strokes to represent it. For instance, element $numbers[3][2] is the number "3" in set 2.

We'll require 2 strokes to render the number "3". So do this:

<?php
session_start();

$numbers=array();

for ($i=0;$i<=9;$i++)
{
    $numbers[$i]=array();

    for ($j=0;$j<=2;$j++)
    {
        $numbers[$i][$j]=array();
    }   
}

$numbers[3][2][0]=array();
$numbers[3][2][0]["width"]="20px";
$numbers[3][2][0]["height"]="20px";
$numbers[3][2][0]["box-shadow"]="5px 2px 0 0px";
$numbers[3][2][0]["margin"]="5px auto 0px 5px";
$numbers[3][2][0]["rotate"]=0;

$numbers[3][2][1]=array();
$numbers[3][2][1]["width"]="20px";
$numbers[3][2][1]["height"]="20px";
$numbers[3][2][1]["box-shadow"]="5px 2px 0 0px";
$numbers[3][2][1]["margin"]="22px auto 0px -4px";
$numbers[3][2][1]["rotate"]=0;


$_SESSION["captcha"]="";

for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;
}

echo $_SESSION["captcha"];
?>

OK, what I did here was to create yet another array inside each array element to store the strokes. $numbers[3][2][0] stores the first stroke, and $numbers[3][2][0] stores the second stroke. These two strokes make up the number "3".

See?

And each stroke is also an array which stores the CSS properties and values of that stroke so we can render them later. That makes $numbers a four-dimensional array.

Wow.

Anyway. there's the entire set. See the number "3" on the bottom row? That's the one we just drew. Number "3", set 2 ($numbers[3][2]).


I've done the same with all the other numbers in all the sets. The code is here, and I'm not about to replicate them on-screen in this blogpost. The good news is, you can just go and copy-paste from the link without having to listen to any more of this drivel. I agree that it's a bit more complicated than it needed to be here.

Let's get on with it!

So I assume you've copied the contents of the entire four-dimensional array. What next? Why, outputting the relevant numbers on the screen, of course! Make the following changes.

<?php
.
.
.
(for the rest of the code, click here)
.
.
.
$_SESSION["captcha"]="";
$captcha_content="";

for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;

    $random_set=rand(0,2);

    $random_degree=rand(-25,25);

}

echo $captcha_content;
?>

Here, we define a new variable $captcha_content which will hold all the HTML codes that we'll be generating. And we'll output $captcha_content in place of $_SESSION["captcha"].

$random_set defines which set of numbers you'll use.

$random_degree is a number that will cause your number to be rotated by that many degrees, randomly.

Next, we'll begin adding data to $captcha_content. It begins with an outer div for each number, which will be totated by $random_degree degrees.
<?php
for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;

    $random_set=rand(0,2);

    $random_degree=rand(-25,25);

    $captcha_content.="<div style=\"display:inline-block;width:30px;height:50px;margin-left:-5px;-ms-transform: rotate(".$random_degree."deg);-webkit-transform: rotate(".$random_degree."deg);transform: rotate(".$random_degree."deg);\">";

    $captcha_content.="</div>";
}

echo $captcha_content;
?>

And the last segment of code, in a Foreach Loop. For each stroke in each selected digit, the HTML and CSS will be written into the containing outer div.
<?php
for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;

    $random_set=rand(0,2);

    $random_degree=rand(-25,25);

    $captcha_content.="<div style=\"display:inline-block;width:30px;height:50px;margin-left:-5px;-ms-transform: rotate(".$random_degree."deg);-webkit-transform: rotate(".$random_degree."deg);transform: rotate(".$random_degree."deg);\">";

    foreach($numbers[$captcha_char][$random_set] as $stroke)
    {
        $captcha_content.="<div style=\"position:absolute;-webkit-border-radius: 63px 63px 63px 63px / 108px 108px 72px 72px;border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;";
        $captcha_content.="width:".$stroke["width"].";";
        $captcha_content.="height:".$stroke["height"].";";
        $captcha_content.="box-shadow:".$stroke["box-shadow"].";";
        $captcha_content.="margin:".$stroke["margin"].";";
        $captcha_content.="-ms-transform: rotate(".$stroke["rotate"]."deg);";
        $captcha_content.="-webkit-transform: rotate(".$stroke["rotate"]."deg);";
        $captcha_content.="transform: rotate(".$stroke["rotate"]."deg);";
        $captcha_content.="\">";
        $captcha_content.="</div>";
    }


    $captcha_content.="</div>";
}

echo $captcha_content;
?>


Now run your code again. This is what you should see.

And that completes your web tutorial. There's a lot more you could do with this - introduce wavy lines or circles to further confuse any bots that hackers might try to sneak past you. But in essence, this is the CAPTCHA.

That was a lot to digest. Sorry to have CAPT you waiting!
T___T

Saturday, 18 July 2015

Web Tutorial: The CAPTCHA (Part 3/4)

You've put up the front-end. You've written a back-end script to output a random 6-digit sequence to your front-end. It's now time to write the next part of your CAPTCHA - the comparison script.

Add the following JavaScript code to your front-end, in the head tag.
        <script>
        function compareCaptcha()
        {
            var xmlhttp;
            if (window.XMLHttpRequest)
            {// code for IE7+, Firefox, Chrome, Opera, Safari
                xmlhttp=new XMLHttpRequest();
            }
            else
            {// code for IE6, IE5
                xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
            }
            xmlhttp.onreadystatechange=function()
            {
                if (xmlhttp.readyState==4 && xmlhttp.status==200)
                {
                    alert(xmlhttp.responseText);
                    getCaptcha();
                }
            }

            xmlhttp.open("POST","tt_comparecaptcha.php",true);
            xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            xmlhttp.send("captcha="+document.getElementById("txtCaptcha").value);
        }


        function getCaptcha()
        {
            var xmlhttp;
            if (window.XMLHttpRequest)
            {// code for IE7+, Firefox, Chrome, Opera, Safari
                xmlhttp=new XMLHttpRequest();
            }
            else
            {// code for IE6, IE5
                xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
            }
            xmlhttp.onreadystatechange=function()
            {
                if (xmlhttp.readyState==4 && xmlhttp.status==200)
                {
                    document.getElementById("lblCaptcha").innerHTML=xmlhttp.responseText;
                }
            }

            xmlhttp.open("POST","tt_getcaptcha.php",true);
            xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            xmlhttp.send();
        }
        </script>


The compareCaptcha() function takes input from the txtCaptcha text box and sends it to the back-end script tt_comparecaptcha.php. If there's a match, or not, it will announce the result via the command alert(xmlhttp.responseText). It will then regenerate the CAPTCHA.

Now modify your front-end with this little nugget, so the compareCaptcha() function will fire off once you click the submit button.
    <body onload="getCaptcha();">
        Please enter the numbers you see below:    <input maxlength="6" id="txtCaptcha" /><input type="button" value="Send" onclick="compareCaptcha();">
        <br />
        <div id="lblCaptcha" style="height:50px;width:160px;border:1px solid #AAAAAA;overflow:hidden;padding-left:20px;"></div>
        <a href="#" onclick="getCaptcha();">Get a new image</a>
    </body>


All done? Excellent. We're going to write the script next. Create a new file and fill it in as follows:

tt_comparecaptcha.php
<?php
session_start();
?>


Again, we have the session_start() line to ensure that we're still running the session. This means that the $_SESSION["captcha"] variable is still active, and still holds the same value as the last time you generated it.

So all we need to do now is the following. This gets the value that the user entered into the text box.
<?php
session_start();

$captcha=$_POST["captcha"];
?>


And this compares it with the $_SESSION["captcha"] variable, then outputs a result message which the earlier JavaScript is supposed to pick up.
<?php
session_start();

$captcha=$_POST["captcha"];

if ($captcha==$_SESSION["captcha"])
{
    echo "Captcha successful!";
}
else
{
    echo "Value did not match.";
}

?>


Now, run your front-end. Type in a correct sequence. What happens?


How about an incorrect sequence?


And there we are! A functioning CAPTCHA. Give yourself a pat on the back!

But hold up - there's something off about this CAPTCHA. It's too neat. A robot could read that sequence easily. So what do we do about that?

Next

Obfuscating your CAPTCHA. We're going to write some code that garbles up the display a bit so it's readable only to a human being. We hope!

Wednesday, 15 July 2015

Web Tutorial: The CAPTCHA (Part 2/4)

You've created the front-end for your CAPTCHA. And also added a getCaptcha() function to call a PHP script tt_getcaptcha.php.

And today, we'll be creating the script for this little sucker. It will be incomplete and we'll need to come back to it sometime in the later parts of this tutorial.

First off, let's start by creating the file in a text editor.

tt_getcaptcha.php
<?php
session_start();
?>


This is important! The session_start() line creates  a session. We need to maintain a session in order to store the value for the CAPTCHA.

More on session_start(): (http://php.net/manual/en/function.session-start.php)
More on session variables in general:  (http://www.w3schools.com/php/php_sessions.asp)

<?php
session_start();

$_SESSION["captcha"]="";
?>


Here, we declare a key captcha in the system-defined array $_SESSION, which was in turn created with session_start(). This is initialized to an empty string.

<?php
session_start();

$_SESSION["captcha"]="";

for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;

}
?>


Now, we have a For loop! It iterates 6 times, as you can see. That's because we're going to generate a 6-dignit CAPTCHA sequence. You can fiddle with this number if you want, but this may mean you have to increase the length of your lblCaptcha div.

So here we generate a random number from 0 to 9, and assign it to the variable $captcha_char. $captcha_char is then appended to the $_SESSION["captcha"] variable.

One last line and we're ready to test!
<?php
session_start();

$_SESSION["captcha"]="";

for ($i=1;$i<=6;$i++)
{
    $captcha_char=rand(0,9);
    $_SESSION["captcha"].=$captcha_char;
}

echo $_SESSION["captcha"];
?>


This basically is the content that the getCaptcha() function is supposed to retrieve.

So run your code. When you load the page, do you see a random set of numbers in the box? Does the same happen if you click the "Get a new image" link?



There you go!

We now have a CAPTCHA generator. But it's still rather primitive, and there's a fair amount of work left to be done. In fact, after we complete the next bit of code, we'll need to improve this further.

Next

Comparing the CAPTCHA to user input. Another script coming right up.



Sunday, 12 July 2015

Web Tutorial: The CAPTCHA (Part 1/4)

Howdy!

Today, your friendly neighborhood web geek will be showing you how to implement a CAPTCHA.

The CAPTCHA's nothing new. You've probably run into it while filling up the odd web form. CAPTCHA stands for "Completely Automated Public Turing test to tell Computers and Humans Apart" and is more or less what it says - there's a distorted sequence of letters and/or numbers, a text box for you to enter that text in, and a button which, if clicked, will compare your input against the sequence to determine if you're human or bot.

For more background, follow this link. (http://en.wikipedia.org/wiki/CAPTCHA)

There are a lot of free CAPTCHA plugins floating around the web that you can implement, but it's important that you know how it works. And really, it's not all that difficult to figure out, and implement your own.

What do I need?

That's the spirit! Now, the CAPTCHA we'll be making is made out of AJAX code, using a combination of HTML, CSS, JavaScript and PHP. Here are the components.

- a front-end layout comprising of a placeholder for your CAPTCHA sequence,a textbox for user input, a button for submission, and a button to reload the CAPTCHA.
- a back-end PHP script to randomly generate a sequence of letters and/or numbers
- a back-end PHP script to compare user input to that sequence.
- a distorter to visually warp the sequence so that it's not easily machine-readable. Some guys like to use a graphics library for this. We'll just be using good old CSS.
- front-end AJAX scripts to send user input to the back-end PHP scripts mentioned above.

Do note that this is not instant code - you'll need to run this setup on a web server.

To begin...

Here's the easiest part: The front-end layout. use the HTML/CSS code below:

tt_captcha.html
<!DOCTYPE html>
<html>
    <head>
        <title>CSS Captcha</title>
    </head>

    <body>
        Please enter the numbers you see below:    <input maxlength="6" id="txtCaptcha" /><input type="button" value="Send">
        <br />
        <div id="lblCaptcha" style="height:50px;width:160px;border:1px solid #AAAAAA;overflow:hidden;padding-left:20px;"></div>
        <a href="#">Get a new image</a>
    </body>
</html>


The HTML code is straightforward. You have a text box, and then you have a button labelled Send which does nothing at the moment. Then below it, you have a lblCaptcha div which holds the CAPTCHA sequence. And below that is a link which, when clicked, is supposed to generate a new sequence. At this moment, this link does nothing either.


Here's what the CSS does for the lblCaptcha div.

border:1px solid #AAAAAA - gives it a nice grey border.
overflow:hidden - makes sure any overlapping stuff is trimmed off.

Now we're going to add an AJAX script to call a PHP script. Add the following JavaScript to your head tag.
    <head>
        <title>CSS Captcha</title>

        <script>
        function getCaptcha()
        {
            var xmlhttp;
            if (window.XMLHttpRequest)
            {// code for IE7+, Firefox, Chrome, Opera, Safari
                xmlhttp=new XMLHttpRequest();
            }
            else
            {// code for IE6, IE5
                xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
            }
            xmlhttp.onreadystatechange=function()
            {
                if (xmlhttp.readyState==4 && xmlhttp.status==200)
                {
                    document.getElementById("lblCaptcha").innerHTML=xmlhttp.responseText;
                }
            }

            xmlhttp.open("POST","tt_getcaptcha.php",true);
            xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            xmlhttp.send();
        }
        </script>

    </head>

    <body>
        Please enter the numbers you see below:    <input maxlength="6" id="txtCaptcha" /><input type="button" value="Send">
        <br />
        <div id="lblCaptcha" style="height:50px;width:160px;border:1px solid #AAAAAA;overflow:hidden;padding-left:20px;"></div>
        <a href="#">Get a new image</a>
    </body>
</html>


The getCaptcha() function calls the tt_getcaptcha.php script without any arguments, signalling to the script to generate a CAPTCHA sequence, and then display the sequence in the lblCaptcha div.

So now add the following snippets to your HTML
    <body onload="getCaptcha();">
        Please enter the numbers you see below:    <input maxlength="6" id="txtCaptcha" /><input type="button" value="Send">
        <br />
        <div id="lblCaptcha" style="height:50px;width:160px;border:1px solid #AAAAAA;overflow:hidden;padding-left:20px;"></div>
        <a href="#" onclick="getCaptcha();">Get a new image</a>
    </body>


This ensures that the page calls the getCaptcha() function to generate a CAPTCHA sequence as soon as the page loads. The same function is called when you hit the "Get a new image" link.

In this function, document.getElementById("lblCaptcha").innerHTML=xmlhttp.responseText is the line that fills the lblCaptcha div with the results returned from the tt_getcaptcha.php script.

Next

Nothing works yet, sparky! We still need to write and test some PHP scripts. Stay tuned!


Saturday, 4 July 2015

Taxi Turmoil

It has been an interesting few months where Singapore's taxi landscape is concerned. Beginning two years back, several third-party taxi-booking mobile apps have emerged on the market. And until Singapore's existing cab companies (Comfort DelGro, Prime, SMRT, TransCab, Premier, CityCab) come to terms with them, there will be a certain amount of upheaval in the industry.

Cab companies in Singapore

Editor's Note: Comfort Cab and CityCab, while being two separate companies, technically belong to the same corporation - Comfort DelGro.

Some third-party taxi booking apps

Currently only a few companies have their own taxi-booking apps - Comfort, TransCab and SMRT. And due to the advantages these third-party apps have over them, these companies have lost ground. Customers are using those apps. Their own cabbies are using those apps.


What are some of these advantages?

Faster booking time due to response of servers. It's more of a network infrastructure problem. A local taxi app is scaled down to provide coverage on this island only, whereas third-party apps are run on servers powerful enough to service the entire globe.

Lack of red tape. Ever been told to hold because "all operators are busy at the moment", and then treated to some appalling music and advertising? Screw all that, now you can interact with cabbies far more directly.

Incentives. Special promotions notwithstanding, consumers tend to pay less (but with a minimum fare) and cabbies stand to earn more.

User interface. I'm not sure about the others, but I have tried SMRT's app. It's functional enough, sure... but how do you compete with the many useful nibblets of information the typical third-party app gives you, like how many taxis are in the vicinity, their locations on the map and etc?

Mixed reactions from cabbies

Some cabbies have expressed the opinion that independent operators driving rented vehicles are taking away a substantial piece of their pie without having to conform to strict licensing and regulations. They'd prefer a more level playing field. These cabbies might just have gotten what they wished for.

And some cabbies welcome the apps - it's a welcome break from having to deal with the restrictions placed upon them by their cab companies, weans them off reliance on those companies and gets them more business. What's not to love?

How does this affect me?

I'm not much of a cab-user. And I tend to hail my cabs rather than book them. But watching all this unfold is utterly fascinating. Computer technology, mobile technology in particular, has once again changed the way we do things.

Talk about a technological drive!
T___T